【SpringBoot3】Spring Security使用mybatis-plus存储用户角色权限,实现动态权限处理

注:本文基于Spring Boot 3.2.1 以及 Spring Security 6.2.1

相关文章

【SpringBoot3】Spring Security 核心概念
【SpringBoot3】Spring Security 常用注解
【SpringBoot3】Spring Security 详细使用实例(简单使用、JWT模式)

一、使用mybatis-plus存储用户角色权限

1)创建相关数据库表
2)从数据库中读取用户、角色、权限数据
3)登录成功返回token
4)访问相关资源时,通过自定义的过滤器解析token,判断权限

1、引入jar包依赖

<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-3-starter</artifactId><version>1.2.21</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency>

2、数据配置以及创建数据表

1)增加数据库链接配置

# 允许循环引用
spring.main.allow-circular-references=true# --- 数据库链接配置
spring.datasource.url=jdbc:mysql://localhost:3306/springsecurity?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource# --- mybatis-plus start
# 关闭MP3.0自带的banner
mybatis-plus.global-config.banner=false
# 主键类型  0:"数据库ID自增",1:"该类型为未设置主键类型", 2:"用户输入ID",3:"全局唯一ID (数字类型唯一ID)", 4:"全局唯一ID UUID",5:"字符串全局唯一ID (idWorker 的字符串表示)";
mybatis-plus.global-config.db-config.id-type=ASSIGN_ID
# 返回类型为Map,显示null对应的字段
mybatis-plus.configuration.call-setters-on-nulls=true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

2)创建数据库

创建5张表:system_menu、system_role、system_role_menu、system_user_role、system_users

这些表可作为前后端分离项目的通用权限表(如果是简单演示2张表就可以了)

CREATE TABLE `system_role` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',`name` varchar(30)  NOT NULL COMMENT '角色名称',`code` varchar(100)  NOT NULL COMMENT '角色权限字符串',`sort` int NOT NULL COMMENT '显示顺序',`status` tinyint NOT NULL COMMENT '角色状态(0正常 1停用)',`remark` varchar(500)  DEFAULT NULL COMMENT '备注',`creator` varchar(64)  DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64)  DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=140 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='角色信息表';CREATE TABLE `system_menu` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '菜单ID',`name` varchar(50)  NOT NULL COMMENT '菜单名称',`permission` varchar(100)  NOT NULL DEFAULT '' COMMENT '权限标识',`type` tinyint NOT NULL COMMENT '菜单类型(1目录 2菜单 3按钮)',`sort` int NOT NULL DEFAULT '0' COMMENT '显示顺序',`parent_id` bigint NOT NULL DEFAULT '0' COMMENT '父菜单ID',`path` varchar(200)  DEFAULT '' COMMENT '路由地址',`icon` varchar(100)  DEFAULT '#' COMMENT '菜单图标',`component` varchar(255)  DEFAULT NULL COMMENT '组件路径',`component_name` varchar(255)  DEFAULT NULL COMMENT '组件名',`status` tinyint NOT NULL DEFAULT '0' COMMENT '菜单状态',`visible` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否可见',`keep_alive` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否缓存',`always_show` bit(1) NOT NULL DEFAULT b'1' COMMENT '是否总是显示',`creator` varchar(64)  DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64)  DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='菜单权限表';CREATE TABLE `system_role_menu` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增编号',`role_id` bigint NOT NULL COMMENT '角色ID',`menu_id` bigint NOT NULL COMMENT '菜单ID',`creator` varchar(64)  DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64)  DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='角色和菜单关联表';CREATE TABLE `system_users` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',`username` varchar(30)  NOT NULL COMMENT '用户账号',`password` varchar(100)  NOT NULL DEFAULT '' COMMENT '密码',`nickname` varchar(30)  NOT NULL COMMENT '用户昵称',`remark` varchar(500)  DEFAULT NULL COMMENT '备注',`email` varchar(50)  DEFAULT '' COMMENT '用户邮箱',`mobile` varchar(11)  DEFAULT '' COMMENT '手机号码',`sex` tinyint DEFAULT '0' COMMENT '用户性别',`avatar` varchar(512)  DEFAULT '' COMMENT '头像地址',`status` tinyint NOT NULL DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',`login_ip` varchar(50)  DEFAULT '' COMMENT '最后登录IP',`login_date` datetime DEFAULT NULL COMMENT '最后登录时间',`creator` varchar(64)  DEFAULT '' COMMENT '创建者',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64)  DEFAULT '' COMMENT '更新者',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE,UNIQUE KEY `idx_username` (`username`,`update_time`) USING BTREE
) ENGINE=InnoDB COMMENT='用户信息表';CREATE TABLE `system_user_role` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增编号',`user_id` bigint NOT NULL COMMENT '用户ID',`role_id` bigint NOT NULL COMMENT '角色ID',`creator` varchar(64)  DEFAULT '' COMMENT '创建者',`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updater` varchar(64)  DEFAULT '' COMMENT '更新者',`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB COMMENT='用户和角色关联表';

springsecurity框架提供了示例的两张表,在文件users.ddl

create table users(username varchar(50) not null primary key,password varchar(500) not null,enabled boolean not null);
create table authorities (username varchar(50) not null,authority varchar(50) not null,constraint fk_authorities_users foreign key(username) references users(username));

3、增加相关Mapper类

@Mapper
public interface MenuMapper extends BaseMapper<MenuDO> {}@Mapper
public interface RoleMapper extends BaseMapper<RoleDO> {}@Mapper
public interface RoleMenuMapper extends BaseMapper<RoleMenuDO> {default List<RoleMenuDO> selectListByRoleId(Collection<Long> roleIds) {return selectList(new LambdaQueryWrapper<RoleMenuDO>().eq(RoleMenuDO::getRoleId, roleIds));}}@Mapper
public interface UserMapper extends BaseMapper<UserDO> {default UserDO selectByUsername(String username) {return selectOne(new LambdaQueryWrapper<UserDO>().eq(UserDO::getUsername, username));}}@Mapper
public interface UserRoleMapper extends BaseMapper<UserRoleDO> {default List<UserRoleDO> selectListByUserId(Long userId) {return selectList(new LambdaQueryWrapper<UserRoleDO>().eq(UserRoleDO::getUserId, userId));}
}

4、增加相关Service类

注意: SecurityExpressionRoot#hasAnyRole方法固定加上前缀ROLE_,因此,如果数据库的roleCode没有前缀ROLE_,则需在查询的时候拼接上去。

spring security的角色和权限是一个概念,只是字符串不一样,角色必须加前缀ROLE_,权限没有这要求。

@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate RoleService roleService;@Autowiredprivate PermissionService permissionService;@Autowiredprivate MenuService menuService;@Resourceprivate UserMapper userMapper;/*** 返回`UserDetails`实现类`UserEntity`*/@Overridepublic UserEntity loadUserByUsername(String username) {UserEntity entity = new UserEntity();UserDO user = getUserByUsername(username);Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(user.getId());List<RoleDO> roles = roleService.getRoleList(roleIds);Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, RoleDO::getId));List<MenuDO> menuList = menuService.getMenuList(menuIds);entity.setId(user.getId());entity.setUsername(user.getUsername());entity.setPassword(user.getPassword());// 注意:SecurityExpressionRoot#hasAnyRole方法固定加上前缀ROLE_,// 因此,如果数据库的roleCode没有前缀ROLE_,则需在查询的时候拼接上去Set<String> roleCodes = convertSet(roles, role -> "ROLE_" + role.getCode());Set<String> permCodes = convertSet(menuList, MenuDO::getPermission);roleCodes.addAll(permCodes);List<String> authorities = roleCodes.stream().filter(StrUtil::isNotBlank).toList();// spring security的角色和权限是一个概念,只是字符串不一样,角色必须加前缀ROLE_,权限没有这要求entity.setAuthorities(AuthorityUtils.createAuthorityList(authorities));return entity;}@Overridepublic UserDO getUserByUsername(String username) {return userMapper.selectByUsername(username);}}/*** 权限 Service 实现类*/
@Service
@Slf4j
public class PermissionServiceImpl implements PermissionService {@Resourceprivate RoleMenuMapper roleMenuMapper;@Resourceprivate UserRoleMapper userRoleMapper;@Resourceprivate RoleService roleService;@Resourceprivate MenuService menuService;@Overridepublic Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {if (CollUtil.isEmpty(roleIds)) {return Collections.emptySet();}// 如果是管理员的情况下,获取全部菜单编号if (roleService.hasAnySuperAdmin(roleIds)) {return convertSet(menuService.getMenuList(), MenuDO::getId);}// 如果是非管理员的情况下,获得拥有的菜单编号return convertSet(roleMenuMapper.selectListByRoleId(roleIds), RoleMenuDO::getMenuId);}@Overridepublic Set<Long> getUserRoleIdListByUserId(Long userId) {return convertSet(userRoleMapper.selectListByUserId(userId), UserRoleDO::getRoleId);}}/*** 角色 Service 实现类*/
@Service
@Slf4j
public class RoleServiceImpl implements RoleService {@Resourceprivate RoleMapper roleMapper;@Overridepublic RoleDO getRole(Long id) {return roleMapper.selectById(id);}@Overridepublic List<RoleDO> getRoleList(Collection<Long> ids) {if (CollectionUtil.isEmpty(ids)) {return Collections.emptyList();}return roleMapper.selectBatchIds(ids);}@Overridepublic boolean hasAnySuperAdmin(Collection<Long> ids) {if (CollectionUtil.isEmpty(ids)) {return false;}return ids.stream().anyMatch(id -> {RoleDO role = getRole(id);return role != null && RoleCodeEnum.isSuperAdmin(role.getCode());});}}/*** 菜单 Service 实现*/
@Service
@Slf4j
public class MenuServiceImpl implements MenuService {@Resourceprivate MenuMapper menuMapper;@Overridepublic List<MenuDO> getMenuList() {return menuMapper.selectList(new LambdaQueryWrapper<>());}@Overridepublic List<MenuDO> getMenuList(Collection<Long> ids) {return menuMapper.selectBatchIds(ids);}}

5、创建自定义UserDetailsService实现类

创建 MyUserDetailsServiceImpl 实现Spring Security接口 UserDetailsService

调用userService方法,返回UserDetails实现类UserEntity

@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {@AutowiredUserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userService.loadUserByUsername(username);}
}

6、创建自定义过滤器JwtTokenFilter

@Component
public class JwtTokenFilter extends OncePerRequestFilter {@AutowiredUserService userService;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 验证token是否有效String token = request.getHeader("token");if (StrUtil.isNotEmpty(token)) {String secret = "0123456789";boolean verify = JWTUtil.verify(token, secret.getBytes());if (!verify) {response.setContentType("application/json;charset=utf-8");response.getWriter().write("{\"code\":401,\"msg\":\"token无效\"}");return;} else {//认证成功,设置用户信息UserEntity user = JWTUtil.parseToken(token).getPayloads().toBean(UserEntity.class);// 模拟获取用户信息,实际情况应该是从数据库查询UserDetails userDetails = userService.loadUserByUsername(user.getUsername());UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(),userDetails.getPassword(),userDetails.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken);}}filterChain.doFilter(request, response);}
}

7、配置HttpSecurity

1)使用自定义的MyUserDetailsServiceImpl
2)使用自定义过滤器JwtTokenFilter
3)登录成功,根据UserEntity生成token,返回给前端。
4)基于 token 机制,所以不需要 Session

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
@Slf4j
public class JwtSecurityConfig {@Autowiredprivate MyUserDetailsServiceImpl userDetailService;@Autowiredprivate JwtTokenFilter jwtTokenFilter;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests((authorize) -> authorize.requestMatchers("/login").permitAll().anyRequest().authenticated()).userDetailsService(userDetailService).exceptionHandling(ex -> ex.authenticationEntryPoint((request, response, authException) -> {log.error("[commence][访问 URL({}) 时,没有登录]", request.getRequestURI(), authException);ServletUtils.writeJSON(response, authException.getMessage());}).accessDeniedHandler((request, response, accessDeniedException) -> {log.warn("[commence][访问 URL({}) 时,用户({}) 权限不够]", request.getRequestURI(),"", accessDeniedException);ServletUtils.writeJSON(response, accessDeniedException.getMessage());})).logout(logout -> logout.invalidateHttpSession(true))// 配置登录页面.httpBasic(Customizer.withDefaults()).formLogin(form -> form.loginPage("/login").permitAll().loginProcessingUrl("/login").successHandler((request, response, authentication) -> {// 登录成功,根据UserEntity生成token,返回给前端log.info("登录成功:{}", authentication);UserEntity principal = (UserEntity) authentication.getPrincipal();String secret = "0123456789";Map<String, Object> payload = new HashMap<>();payload.put("id", principal.getId());payload.put("username", principal.getUsername());String token = JWTUtil.createToken(payload, secret.getBytes());response.setContentType("application/json;charset=UTF-8");Map<String, Object> map = new HashMap<>();map.put("token", token);response.getWriter().write(JSONUtil.toJsonStr(map));}).failureHandler((request, response, authentication) -> {log.info("登录失败", authentication);}));// CSRF 禁用,因为不使用 Sessionhttp.csrf(AbstractHttpConfigurer::disable);// 基于 token 机制,所以不需要 Sessionhttp.sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS));http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}}

8、总结

1、使用postman请求登录接口,密码验证成功后,通过 successHandler 返回token
2、将token当作Header参数,请求 /admin 等需要权限的接口地址
3、 定义过滤器JwtTokenFilter接收到请求,解析Token校验账户权限,设置当前登录用户信息
4、权限校验完成!

二、动态权限处理

Spring Security 提供了拦截器,用于控制对安全对象的访问,这些安全对象可以是方法调用或 Web 请求。在调用之前,AuthorizationManager 实例会作出决策,判断调用是否允许继续。此外,AuthorizationManager 实例还会在调用之后作出决策,判断给定的值是否可以返回。

动态权限可以通过创建自定义类(如 MyAuthorizationManager)实现接口 AuthorizationManager,在实现类的 check() 方法中判断用户是否有请求资源的权限。

通常是结合数据库中配置的数据判断当前用户是否有当前请求URI的权限

1、创建自定义权限处理类

MyAuthorizationManager,实现接口 AuthorizationManager

@Component
@Slf4j
public class MyAuthorizationManager implements AuthorizationManager<HttpServletRequest> {@Autowiredprivate PermissionService permissionService;@Overridepublic AuthorizationDecision check(Supplier<Authentication> authentication, HttpServletRequest request) {log.info("Checking authorization {}", request);String requestURI = request.getRequestURI();//根据这些信息 和业务写逻辑即可 最终决定是否授权 isGrantedboolean isGranted = true;boolean isGranted = true;if (requestURI.equals("/login") || requestURI.equals("/logout")) {return new AuthorizationDecision(isGranted);}//当前用户的权限信息 比如角色Collection<? extends GrantedAuthority> authorities = authentication.get().getAuthorities();// 当前用户权限代码List<String> userPermCodes = authorities.stream().map(GrantedAuthority::getAuthority).toList();// 查询当前被访问的URL:需要哪些权限才能访问List<String> codes = permissionService.getPermissions(requestURI);if(codes == null || codes.isEmpty() ){return new AuthorizationDecision(true);}// 如果包含用户所拥有的权限,则授权通过,否则授权失败boolean containsed = CollectionUtil.containsAny(codes, userPermCodes);if(!containsed){isGranted = false;}return new AuthorizationDecision(isGranted);}
}

2、配置 HttpSecurity 添加过滤器

添加过滤器 AuthorizationFilter ,传入自定义权限处理类 MyAuthorizationManager

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@Configuration
@Slf4j
public class JwtSecurityConfig {@Autowiredprivate MyAuthorizationManager  authorizationManager;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {// -- 省略其他代码 -- //http.addFilter(new AuthorizationFilter(authorizationManager));return http.build();}}

3、总结

通过以上配置,每次接口请求,都会经过MyAuthorizationManager ,用户可以自行判断是否通过授权。

通过返回值 new AuthorizationDecision(isGranted),决定是否授权成功!

参考

  • https://docs.spring.io/spring-security/reference/index.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/702417.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【5G NR】【一文读懂系列】移动通讯中使用的信道编解码技术-LDPC编码介绍(一)

概述 低密度校验码&#xff08;LDPC码&#xff09;是一种前向纠错码&#xff0c;LDPC码最早在20世纪60年代由Gallager在他的博士论文中提出&#xff0c;但限于当时的技术条件&#xff0c;缺乏可行的译码算法&#xff0c;此后的35年间基本上被人们忽略&#xff0c;其间由Tanner…

轻松玩转树莓派Pico之九、RP2040-SMP自定义工程创建

[toc]## 1、工程创建 运行完 FreeRTOS-SMP-Demos 后&#xff0c;我们对 SMP 运行有了一定的了解&#xff0c;接下来我们自己创建工程编译运行。 按照前文 轻松玩转树莓派Pico之二、创建自己的pico工程项目 一文创建 pico_smp 项目。 创建 pico_smp.c&#xff0c;并输入&#x…

浅谈数据分析工具在智慧城市中的作用

随着城市化、技术进步和人口不断增长&#xff0c;智慧城市已成为当今世界主要技术发展之一。 智慧城市设备依靠描述模型对城市环境产生的大量数据进行数据分析。 在这种城市景观中&#xff0c;智慧城市是技术和可持续的城市地区&#xff0c;利用信息和通信技术(ICT)来改善城市…

C语言每日一题(61)盛最多水的容器

题目链接 力扣 11 盛最多水的容器 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水…

Eureka服务搭建

1️⃣搭建服务 引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>启动类加注解 EnableEurekaServer SpringBootApplication public…

【服务发现--ingress】

1、ingress介绍 Ingress 提供从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源所定义的规则来控制。 Ingress 是对集群中服务的外部访问进行管理的 API 对象&#xff0c;典型的访问方式是 HTTP。 Ingress 可以提供负载均衡、SSL 终结和基于名称的虚拟…

基于Qt实现TCP和UDP客户端与服务端的完整指南

在当今的软件开发领域&#xff0c;网络通信已经成为了不可或缺的一部分。无论是在物联网(IoT)、企业级应用还是简单的客户端与服务端通信场景中&#xff0c;有效且高效的数据传输都是至关重要的。Qt&#xff0c;作为一个跨平台的C框架&#xff0c;不仅提供了丰富的用户界面组件…

K线实战分析系列之十一:行情力量不足——平头形态

K线实战分析系列之十一&#xff1a;行情力量不足——平头形态 一、平头形态二、不同形态与平头形态的叠加三、总结平头形态 一、平头形态 前一根K线具有较长的实体&#xff0c;后一根K线的实体比较小&#xff0c;无论是多头还是空头的力量到第二根K线都被瓦解了多头上攻&#…

【数据结构与算法】(21)高级数据结构与算法设计之 Dynamic-Programming 动态规划算法 代码示例与详细讲解

目录 4.3 Dynamic-Programming1) Fibonacci降维 2) 最短路径 - Bellman-Ford3) 不同路径-Leetcode 62降维 4) 0-1 背包问题降维 5) 完全背包问题降维 6) 零钱兑换问题-Leetcode322降维零钱兑换 II-Leetcode 518 7) 钢条切割问题降维类似题目 Leetcode-343 整数拆分 8) 最长公共…

HashMap学习

hashmap 1、hashmap 与 hashtable 的区别2、hashmap基本类型与包装类常用实现变量介绍初始容量和负载因子红黑树和链表转化HashMap的内部数据结构HashMap内部哈希算法 参考文章 1、hashmap 与 hashtable 的区别 线程安全null值执行效率 2、hashmap hashmap是java中常用的集合…

寻找连续区间(C 语言)【数组区间处理】

题目来自于博主算法大师的专栏&#xff1a;最新华为OD机试C卷AB卷OJ&#xff08;CJavaJSPy&#xff09; https://blog.csdn.net/banxia_frontend/category_12225173.html 题目 给定一个含有 N 个正整数的数组&#xff0c; 求出有多少个连续区间&#xff08;包括单个正整数&am…

python 基础知识点(蓝桥杯python科目个人复习计划51)

今日复习计划&#xff1a;做复习题 例题1&#xff1a;大石头的搬运工 问题描述&#xff1a; 在一款名为“大石头的搬运工”的游戏中&#xff0c;玩家需要 操作一排n堆石头&#xff0c;进行n - 1轮游戏。 每一轮&#xff0c;玩家可以选择一堆石头&#xff0c;并将其移动到任…

【生活】浅浅记录

各位小伙伴们好鸭&#xff0c;今天不是技术文章&#xff0c;浅浅记录一下最近几个月的收获&#x1f60a; 新的一年&#xff0c;一起努力&#xff0c;加油加油&#xff01;

分库分表(以MySQL为例说明)

1、为什么要分库&#xff1f; ①减轻磁盘压力 ②单库并发连接请求数有限&#xff0c;如果存在过多连接会出现too many connections的问题 2、为什么要分表&#xff1f; ①一般单表数据量超千万很大了&#xff0c;单表数据量过大会出现性能瓶颈&#xff0c;建索引、SQL优化、数据…

tinymce问题处理

Vite构建工具下Tinymce踩坑指南 解决方案是在路劲前面增加/&#xff0c;这个跟上面链接有些区别&#xff0c;区别原因应该是如果路由采用的是createWebHashHistory则应该去掉/&#xff0c;如果是createWebHistory则应该加上/ 页面引用,一种异步加载&#xff0c;一种同步加载&…

深度学习的启航:从零到一的完全指南

深度学习的启航&#xff1a;从零到一的完全指南 深度学习近年来已成为人工智能领域最耀眼的明星&#xff0c;其在图像识别、自然语言处理、游戏以及无人驾驶等领域取得的进展令人瞩目。如果你对深度学习充满好奇&#xff0c;想要开启一段全新的学习旅程&#xff0c;这篇文章将…

睿易售前初级认证课程R1.0睿网络命名选型-2023练习题答案

1、RG-RAP6260(G)可以用在什么场景下?【多选题】 A、别墅花园 B、冷库 C、大型仓库 D、海边 A B C D 2、AP选型要关注什么?【多选题】 A、使用场景 B、带机量 C、Wi-Fi5还是Wi-Fi6 D、千兆还是百兆 A B C D 3、带机60终端以上的Wi-Fi6千兆吸顶AP选哪款【多选题】 A、…

LeetCode 热题 100 | 二叉树(二)

目录 1 543. 二叉树的直径 2 102. 二叉树的层序遍历 3 108. 将有序数组转换为二叉搜索树 菜鸟做题&#xff0c;语言是 C 1 543. 二叉树的直径 这道题和 124. 二叉树中的最大路径和 太像了 题眼&#xff1a;二叉树的 直径 是指树中任意两个节点之间 最长路径的长度 。…

JS基础(三)-操作和流程控制

一 操作网页元素的步骤 1. 查找网页元素 给标签设置id属性&#xff0c;一个网页中的id值不允许重复 <button id"btn">按钮</button> 2. 给按钮绑定事件&#xff0c;监听用户操作 btn.onclick function(){ 一旦监听到用户的…

人工智能 — 特征选择、特征提取、PCA

目录 一、特征选择1、定义2、原因3、做法4、生成过程5、停止条件 二、特征提取三、PCA 算法1、零均值化&#xff08;中心化&#xff09;2、方差3、协方差4、协方差矩阵5、对协方差矩阵求特征值、特征矩阵6、对特征值进行排序7、评价模型8、代码实现9、sklearn 库10、鸢尾花实例…