大江网站建设/百度优化大师
说明
SpringSecurity的权限校是基于SpringSecurity的安全认证的详解说明(附完整代码)
(https://blog.csdn.net/qq_51076413/article/details/129102660)的讲解,如果不了解SpringSecurity是怎么认证,请先看下【SpringSecurity的安全认证的详解说明(附完整代码)
】
基于SpringSecurity的安全认证的详解说明(附完整代码),继续讲解权限的校验
开启权限校验
@EnableGlobalMethodSecurity(prePostEnabled = true)
- 首先在
SecurityConfig
类上添加@EnableGlobalMethodSecurity(prePostEnabled = true)
注解- 表示开启方法的权限校验(默认是关闭的,这里要开启校验)
下面以继承WebSecurityConfigurerAdapter
类配置的方法已过时,可以使用后面的配置
/*** @ApiNote: SpringSecurity配置信息* @Author: 陌路* @Date: 2023/2/18 12:14* @Tool: Created by IntelliJ IDEA*/
//@Configuration
// 开启方法权限校验,默认是关闭的,这里要开启校验
//@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {// 注入自定义的过滤器,在用户名和密码认证之前执行(UsernamePasswordAuthenticationFilter之前)@Resourceprivate TokenAuthorityFilter tokenAuthorityFilter;@Resourceprivate AuthenticationEntryPoint authenticationEntryPoint;@Resourceprivate AccessDeniedHandler accessDeniedHandler;/*** @apiNote: 注入密码加密工具*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** @apiNote: 注入AuthenticationManager对象来实现登录逻辑管理*/@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}/*** @apiNote: 配置请求认证和拦截*/@Overrideprotected void configure(HttpSecurity http) throws Exception {// 关闭Security的CSRF功能防御http.csrf().disable()// 不通过Session获取SecurityContext.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 配置接口权限,getUserList必须具备system:dept:index权限才允许访问,否则不允许访问.mvcMatchers("/getUserList").hasAnyAuthority("system:dept:index")// 允许所有用户访问登录路径:anonymous(匿名访问,即允许未登录时访问,登录时则不允许访问).antMatchers("/user/login").anonymous()//匿名访问(未登录未认证的)// 除以上请求路径外,其他所有请求都必须经过认证才能访问成功.anyRequest().authenticated();// 添加自定义的请求过滤器(tokenAuthorityFilter)并定义在指定哪个过滤器(UsernamePasswordAuthenticationFilter)执行前执行http.addFilterBefore(tokenAuthorityFilter, UsernamePasswordAuthenticationFilter.class);// 添加自定义异常处理器http.exceptionHandling()// 认证失败处理.authenticationEntryPoint(authenticationEntryPoint)// 授权失败处理.accessDeniedHandler(accessDeniedHandler);// 允许跨域配置http.cors();}// 测试密码的加密和密码的验证public static void main(String[] args) {BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();// 加密后的密文,每次加密结果都不一样,因为加密时会生成随机盐值String encode = passwordEncoder.encode("123456");// 校验用户输入的密码和加密后的密码是否一样,一样返回true,否则返回falseboolean matches = passwordEncoder.matches("123456", encode);System.out.println("encode = " + encode);System.out.println("matches = " + matches);}
}
上面的配置方式已过时,可以使用下面的配置
/*** @ApiNote: SpringSecurity配置信息* @Author: 陌路* @Date: 2023/2/18 12:14* @Tool: Created by IntelliJ IDEA*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {@Resourceprivate TokenAuthorityFilter tokenAuthorityFilter;@Resourceprivate AuthenticationConfiguration authenticationConfiguration;@Resourceprivate AuthenticationEntryPoint authenticationEntryPoint;@Resourceprivate AccessDeniedHandler accessDeniedHandler;/*** @apiNote: 注入AuthenticationManager对象来实现登录逻辑管理*/@Beanpublic AuthenticationManager authenticationManager() throws Exception {AuthenticationManager authenticationManager = authenticationConfiguration.getAuthenticationManager();return authenticationManager;}/*** @apiNote: 注入密码加密工具*/@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 配置接口权限,getUserList必须具备system:dept:index权限才允许访问,否则不允许访问.mvcMatchers("/getUserList").hasAnyAuthority("system:dept:index")// 允许所有用户访问登录路径:anonymous(匿名访问,即允许未登录时访问,登录时则不允许访问).antMatchers("/user/login").anonymous()// 除以上请求路径外,其他所有请求都必须经过认证才能访问成功.anyRequest().authenticated().and()// 添加自定义的请求过滤器(tokenAuthorityFilter)并定义在指定哪个过滤器(UsernamePasswordAuthenticationFilter)执行前执行.addFilterBefore(tokenAuthorityFilter, UsernamePasswordAuthenticationFilter.class);// 添加异常处理器http.exceptionHandling()// 授权异常处理器.accessDeniedHandler(accessDeniedHandler)// 认证异常处理器.authenticationEntryPoint(authenticationEntryPoint);// 运行跨域配置http.cors();return http.build();}
}
定义权限信息
/*** @ApiNote: 菜单对象实体类,对应表(sys_menu)* @Author: 陌路* @Date: 2023/2/19 11:33* @Tool: Created by IntelliJ IDEA*/
@Data
@NoArgsConstructor
@TableName("sys_menu")
@ToString(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Menu implements Serializable {private static final long serialVersionUID = -54979041104113736L;@TableIdprivate Long id;private String menuName;//菜单名private String path;//路由地址private String component;//组件路径private String visible;//菜单状态(0显示 1隐藏)private String status;//菜单状态(0正常 1停用)private String perms;//权限标识private String icon; //菜单图标private Long createBy;private Date createTime;private Long updateBy;private Date updateTime;private Integer delFlag;//是否删除(0未删除 1已删除)private String remark;//备注
}
根据用户id获取权限信息
/*** @ApiNote: 菜单权限$* @Author: 陌路* @Date: 2023/2/19 11:35* @Tool: Created by IntelliJ IDEA*/
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {/*** @apiNote: 根据用户id获取权限* @param: userId* @return: list*/List<String> queryPermsByUserId(Long userId);
}
根据用户id获取所拥有的权限信息
<select id="queryPermsByUserId" parameterType="long" resultType="string">SELECT DISTINCT M.PERMSFROM SYS_USER_ROLE ULEFT JOIN SYS_ROLE R ON U.ROLE_ID = R.IDLEFT JOIN SYS_ROLE_MENU RM ON U.ROLE_ID = RM.ROLE_IDLEFT JOIN SYS_MENU M ON M.ID = RM.MENU_IDWHERE USER_ID = #{userId}AND R.STATUS = 0AND M.STATUS = 0</select>
- 用户登录成功时,根据当前用户信息获取该用户所拥有的的权限,并将权限封装到
LoginUser
中- 在
LoginUser
中再将权限信息通过SpringSecurity
提供的方法(getAuthorities
)配置到SpringSecurity
管理中menuMapper.queryPermsByUserId(user.getId());
根据用户id获取所拥有的权限
配置文件配置MyBatis-Plus的信息
spring:# 数据库链接配置datasource:url: jdbc:mysql://127.0.0.1:3306/security?characterEncoding=utf8&serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverapplication:name: SpringSecurity-JWT# 热部署devtools:restart:enabled: trueadditional-paths: src/main/java# 服务端口
server:port: 8090# token有效期为5分钟
token:expire: 300000# 用于生成JWT的盐值
jwt:secret: 1234567890# xml文件路径地址
mybatis-plus:mapper-locations: classpath*:/mapper/**/*.xml
/*** @ApiNote: 用户数据认证* @Author: 陌路* @Date: 2023/2/18 11:34* @Tool: Created by IntelliJ IDEA*/
@Service("userDetailsImpl")
public class UserDetailsImpl implements UserDetailsService {@Resourceprivate UserMapper userMapper;@Resourceprivate MenuMapper menuMapper;/*** @apiNote: 根据用户名获取用户数据* @param: username 用户名* @return: UserDetails*/@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名查询用户数据User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserName, username));ApiUtils.checkParamsIsEmpty("未获取到用户数据,请检查用户名和密码是否正确!", user);// 根据用户信息查询相关权限List<String> list = menuMapper.queryPermsByUserId(user.getId());// 将用户数据和用户权限数据封装到LoginUser中并返回return new LoginUser(user, list);}
}
将权限封装到LoginUser
中
将权限信息封装到
LoginUser
中
private List<String> permissions;
// 用户权限信息private List<GrantedAuthority> authorityList;
//全部权限信息对象集合public Collection<? extends GrantedAuthority> getAuthorities() {...}
//封装成SpringSecurity
需要的格式public LoginUser(User user, List<String> permissions) {...}
// 提供有参构造
/*** @ApiNote: 封装登录用户数据* @Author: 陌路* @Date: 2023/2/18 11:55* @Tool: Created by IntelliJ IDEA*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public class LoginUser implements UserDetails {// 实现SpringSecurity提供的UserDetails接口来管理用户数据private User user;private Long expire; // 过期时间private String token; // tokenprivate List<String> permissions; // 用户权限信息private List<GrantedAuthority> authorityList;/*** @apiNote: 获取当前登录用户信息*/public static LoginUser getLoginUser() {LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();return ApiUtils.getObj(loginUser, new LoginUser());}public LoginUser(User user, List<String> permissions) {this.user = user;this.permissions = permissions;}/*** @apiNote: 用户权限信息*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {// 把permissions中的String权限封装到SimpleGrantedAuthority对象中(GrantedAuthority的实现类)if (ObjUtil.isEmpty(this.authorityList)) {this.authorityList = this.permissions.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());}return this.authorityList;}/*** @apiNote: 获取用户密码*/@Overridepublic String getPassword() {return user.getPassword();}/*** @apiNote: 获取用户名*/@Overridepublic String getUsername() {return user.getUserName();}/*** @apiNote: 是否未过期(true : 未过期 , false : 已过期)*/@Overridepublic boolean isAccountNonExpired() {return true;}/*** @apiNote: 是否锁定*/@Overridepublic boolean isAccountNonLocked() {return true;}/*** @apiNote: 是否超时(true : 未超时 , false : 已超时)*/@Overridepublic boolean isCredentialsNonExpired() {return true;}/*** @apiNote: 当前用户是否可用(true:可用,false:不可用)*/@Overridepublic boolean isEnabled() {return true;}
}
权限校验配置
- 在自定义的请求过滤器中,配置封装好的权限信息即可
new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
// 传入封装好了的权限信息
/*** @ApiNote: 请求过滤器:是否认证是否有权访问* @Author: 陌路* @Date: 2023/2/18 13:04* @Tool: Created by IntelliJ IDEA*/
@Component
public class TokenAuthorityFilter extends OncePerRequestFilter {@Resourceprivate TokenUtils tokenUtils;@Resourceprivate ContextLoader contextLoader;/*** @apiNote: 请求过滤器*/@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 获取token数据String authorityToken = ApiUtils.getStr(request.getHeader("Authorization"), request.getParameter("Authorization"));// token为空直接放行if (StringUtils.isBlank(authorityToken)) {filterChain.doFilter(request, response);return;}// 解析token数据得到userIdString userId = tokenUtils.getUserId(authorityToken);// 从缓存中获取用户信息LoginUser loginUser = contextLoader.getCacheUser(userId + "_TOKEN_" + authorityToken);ApiUtils.checkParamsIsEmpty("请求失败,认证已过期!", loginUser, loginUser.getUser());// 将用户信息封装到SecurityContextHolder中//principal:用户数据,credentials:,authenticated:权限信息(loginUser.getAuthorities())UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());SecurityContextHolder.getContext().setAuthentication(authenticationToken);filterChain.doFilter(request, response);}
}
使用权限校验规则
- 接口校验:在请求接口方法上添加
@PreAuthorize("hasAnyAuthority('system:dept:index')")
即可- 使用
SpringSecurity
默认配置校验规则
/*** @ApiNote: 请求接口控制器* @Author: 陌路* @Date: 2023/2/18 9:53* @Tool: Created by IntelliJ IDEA*/
@RestController
@RequestMapping("/user/*")
public class IndexController {@Resourceprivate UserService userService;/*** @apiNote: 获取用户列表* @return: Result*/@GetMapping("getUserList")@PreAuthorize("hasAnyAuthority('system:dept:index')") // SpringSecurity校验配置
// @PreAuthorize("@api.hasAuthority('system:dept:index')") // 自定义校验配置public Result getUserList() {return Result.ok(userService.queryList());}/*** @apiNote: 用户登录接口* @param: User对象实体* @return: Result*/@PostMapping("login")public Result login(@RequestBody User user) {return userService.login(user);}/*** @apiNote: 用户退出登录* @return: Result*/@GetMapping("logout")public Result logout() {return Result.res(userService.logout());}
}
SpringSecurity异常处理
- 认证失败: 实现
SpringSecurity
提供的AuthenticationEntryPoint
接口中的commence
方法来处理认证失败后的业务- 统一处理:统一返回JSON异常提示信息
- 在SpringSecurity配置类(
SecurityConfiguration
)中添加http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
即可
/*** @ApiNote: 认证失败处理类* @Author: 陌路* @Date: 2023/2/19 12:25* @Tool: Created by IntelliJ IDEA*/
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {/*** @apiNote: 认证失败处理* @return: JSON(认证失败,请重新登录)*/@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {String authExceptionMessage = authException.getMessage();authExceptionMessage = StringUtils.isBlank(ApiUtils.getStr(authExceptionMessage)) ? "认证失败,请重新登录!" : authExceptionMessage;String jsonStr = JSONUtil.toJsonStr(Result.err(HttpStatus.UNAUTHORIZED.value(), authExceptionMessage));ApiUtils.printJsonMsg(jsonStr, response);}
}
授权异常统一处理
- 授权失败:实现
SpringSecurity
提供的AccessDeniedHandler
接口中的handle
方法来处理授权失败业务- 统一处理:统一返回JSON异常提示信息
- 在SpringSecurity配置类(
SecurityConfiguration
)中添加http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
即可
/*** @ApiNote: 授权失败处理类* @Author: 陌路* @Date: 2023/2/19 12:53* @Tool: Created by IntelliJ IDEA*/
@Component
public class AccessDeniedHandlerImpl implements AccessDeniedHandler {/*** @apiNote: 授权失败处理* @return: JSON(您的权限不足,无法访问资源!)*/@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {String accessDeniedExceptionMessage = accessDeniedException.getMessage();accessDeniedExceptionMessage = StringUtils.isBlank(ApiUtils.getStr(accessDeniedExceptionMessage)) ? "不允许访问" : accessDeniedExceptionMessage;accessDeniedExceptionMessage = accessDeniedExceptionMessage.equals("不允许访问") ? "您的权限不足,无法访问资源!" : accessDeniedExceptionMessage;String jsonStr = JSONUtil.toJsonStr(Result.err(HttpStatus.FORBIDDEN.value(), accessDeniedExceptionMessage));ApiUtils.printJsonMsg(jsonStr, response);}
}
配置自定义校验规则
- 需要自定义配置权限校验规则的可以按照
SpringSecurity
中的hasAuthority.()..
方法自定义- 注意点:定义Bean的时候
@Component("api")
中一定要添加上值,调用时需要根据@Component
中的值来调用当前的校验规则
/*** @ApiNote: 自定义权限校验配置$* @Author: 陌路* @Date: 2023/2/19 14:25* @Tool: Created by IntelliJ IDEA*/
@Component("api")
public class ApiExpressionRoot {/*** @apiNote: 定义权限校验规则* @param: authority:接口方法中的权限码* @return: true:有权限允许方法。false:无权限不允许访问*/public final boolean hasAuthority(String authority) {// 获取权限信息列表(TokenAuthorityFilter)已经将数据存到到LoginUser,直接调用即可List<String> permissions = LoginUser.getLoginUser().getPermissions();if (ObjUtil.isNotEmpty(permissions)) {Set<String> set = new HashSet<>(permissions);// 判断用户权限中是否包含此权限代码return set.contains(authority);}return false;}
}
自定义校验规则的使用
- 直接在请求接口方法上添加上自定义校验规则方法即可
- 注意:调用时需要使用SPEL表达式调用,否则不会生效
- 使用
@PreAuthorize("@api.hasAuthority('system:dept:index')")
其中@api
是自定义配置类ApiExpressionRoot
中定义的@Component("api")
,这时自定义规则就会生效了
/*** @apiNote: 获取用户列表* @return: Result*/@GetMapping("getUserList") //@PreAuthorize("hasAnyAuthority('system:dept:index')") // SpringSecurity校验配置@PreAuthorize("@api.hasAuthority('system:dept:index')") // 自定义校验配置public Result getUserList() {return Result.ok(userService.queryList());}
调用成功
用到的SQL语句脚本
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;-- ----------------------------
-- Table structure for sys_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`menu_name` varchar(64) NOT NULL DEFAULT 'NULL' COMMENT '菜单名',`path` varchar(200) DEFAULT NULL COMMENT '路由地址',`component` varchar(255) DEFAULT NULL COMMENT '组件路径',`visible` char(1) DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',`status` char(1) DEFAULT '0' COMMENT '菜单状态(0正常 1停用)',`perms` varchar(100) DEFAULT NULL COMMENT '权限标识',`icon` varchar(100) DEFAULT '#' COMMENT '菜单图标',`create_by` bigint NULL DEFAULT NULL,`create_time` datetime NULL DEFAULT NULL,`update_by` bigint NULL DEFAULT NULL,`update_time` datetime NULL DEFAULT NULL,`del_flag` int NULL DEFAULT 0 COMMENT '是否删除(0未删除 1已删除)',`remark` varchar(500) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '菜单表';-- ----------------------------
-- Records of sys_menu
-- ----------------------------
INSERT INTO `sys_menu` VALUES (2, '部门管理', 'dept', 'system/dept/index', '0', '0', 'system:dept:index', '#', NULL, NULL, NULL, NULL, 0, NULL);
INSERT INTO `sys_menu` VALUES (3, '测试', 'test', 'system/test/index', '0', '0', 'system:test:index', '#', NULL, NULL, NULL, NULL, 0, NULL);-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(128) DEFAULT NULL,`role_key` varchar(100) DEFAULT NULL COMMENT '角色权限字符串',`status` char(1) DEFAULT '0' COMMENT '角色状态(0正常 1停用)',`del_flag` int NULL DEFAULT 0 COMMENT 'del_flag',`create_by` bigint NULL DEFAULT NULL,`create_time` datetime NULL DEFAULT NULL,`update_by` bigint NULL DEFAULT NULL,`update_time` datetime NULL DEFAULT NULL,`remark` varchar(500) DEFAULT NULL COMMENT '备注',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COMMENT = '角色表';-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (3, 'CEO', 'ceo', '0', 0, NULL, NULL, NULL, NULL, NULL);
INSERT INTO `sys_role` VALUES (4, 'Coder', 'coder', '0', 0, NULL, NULL, NULL, NULL, NULL);-- ----------------------------
-- Table structure for sys_role_menu
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_menu`;
CREATE TABLE `sys_role_menu` (`role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',`menu_id` bigint NOT NULL DEFAULT 0 COMMENT '菜单id',PRIMARY KEY (`role_id`, `menu_id`) USING BTREE,INDEX `menu_id`(`menu_id`) USING BTREE-- CONSTRAINT `menu_id` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,-- CONSTRAINT `role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4;-- ----------------------------
-- Records of sys_role_menu
-- ----------------------------
INSERT INTO `sys_role_menu` VALUES (3, 2);
INSERT INTO `sys_role_menu` VALUES (3, 3);
INSERT INTO `sys_role_menu` VALUES (4, 3);-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` varchar(64) DEFAULT 'NULL' COMMENT '用户名',`nick_name` varchar(64) DEFAULT 'NULL' COMMENT '昵称',`password` varchar(64) DEFAULT 'NULL' COMMENT '密码',`status` char(1) DEFAULT '0' COMMENT '账号状态(0正常 1停用)',`email` varchar(64) DEFAULT NULL COMMENT '邮箱',`phonenumber` varchar(32) DEFAULT NULL COMMENT '手机号',`sex` char(1) DEFAULT NULL COMMENT '用户性别(0男,1女,2未知)',`avatar` varchar(128) DEFAULT NULL COMMENT '头像',`user_type` char(1) DEFAULT '1' COMMENT '用户类型(0管理员,1普通用户)',`create_by` bigint NULL DEFAULT NULL COMMENT '创建人的用户id',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',`update_by` bigint NULL DEFAULT NULL COMMENT '更新人',`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',`del_flag` int NULL DEFAULT 0 COMMENT '删除标志(0代表未删除,1代表已删除)',PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COMMENT = '用户表';-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (3, 'molu', 'molu', '$2a$10$1s.BtZ6Ay/nU7VB/cgaTv.PiYezHYWOLntRsUqFgter/hsMDViZ0K', '0', NULL, NULL, NULL, NULL, '1', NULL, NULL, NULL, NULL, 0);-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户id',`role_id` bigint NOT NULL DEFAULT 0 COMMENT '角色id',PRIMARY KEY (`user_id`, `role_id`) USING BTREE,INDEX `rold_id`(`role_id`) USING BTREE-- CONSTRAINT `rold_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,-- CONSTRAINT `user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8mb4;-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (3, 3);SET FOREIGN_KEY_CHECKS = 1;
相关文章:

SpringSecurity的权限校验详解说明(附完整代码)
说明 SpringSecurity的权限校是基于SpringSecurity的安全认证的详解说明(附完整代码) (https://blog.csdn.net/qq_51076413/article/details/129102660)的讲解,如果不了解SpringSecurity是怎么认证,请先看下【SpringSecurity的安…...

Java-集合(5)
Map接口 JDK8 Map接口实现子类的特点 Map和Collection是并列关系,Map用于保存具有映射关系的数据:Key-ValueMap中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中Map中的key不允许重复,原因和HashSet一样Map…...

研制过程评审活动(四)设计定型阶段
1、设计定型阶段主要任务 设计定型的主要任务是对武器装备性能和使用要求进行全面考核,以确认产品是否达到《研制任务书》和《研制合同》的要求。 设计定型阶段应最终确定《产品规范》、《工艺规范》和《材料规范》的正式版本,并形成正式的全套生产图样、有关技术文件及目…...

【Linux】进程替换
文章目录进程程序替换替换原理替换函数函数返回值函数命名理解在makefile文件中一次生成两个可执行文件总结:程序替换时运行其它语言程序进程程序替换 程序要运行要先加载到内存当中 , 如何做到? 加载器加载进来,然后程序替换 为什么? ->冯诺依曼 因为CPU读取数据的时候只…...

LeetCode171-Excel表列序号(进制转换问题)
LeetCode171-Excel表列序号1、问题描述2、解题思路:进制转换3、代码实现1、问题描述 给你一个字符串columnTitle,表示Excel表格中得列名称。返回该列名称对应得列序号。 例如: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 …...

React SSR
ReactDOMServer 参考链接:https://zh-hans.reactjs.org/docs/react-dom-server.html ReactDOMServer 对象允许你将组件渲染成静态标记。通常,它被使用在 Node 服务端上 // ES modules import * as ReactDOMServer from react-dom/server; // CommonJS v…...

如何系统地优化页面性能
页面优化,其实就是要让页面更快地显示和响应。由于一个页面在它不同的阶段,所侧重的关注点是不一样的,所以如果要讨论页面优化,就要分析一个页面生存周期的不同阶段。 通常一个页面有三个阶段:加载阶段、交互阶段和关…...

Vulnhub 渗透练习(八)—— THE ETHER: EVILSCIENCE
环境搭建 环境下载 靶机和攻击机网络适配都选 NAT 即可。 信息收集 主机扫描 两个端口,22 和 80,且 apache httpd 2.4.0~2.4.29 存在换行解析漏洞。 Apache HTTPD是一款HTTP服务器,它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中…...

华为OD机试题 - 水仙花数 2(JavaScript)| 代码+思路+重要知识点
最近更新的博客 华为OD机试题 - 字符串加密(JavaScript) 华为OD机试题 - 字母消消乐(JavaScript) 华为OD机试题 - 字母计数(JavaScript) 华为OD机试题 - 整数分解(JavaScript) 华为OD机试题 - 单词反转(JavaScript) 使用说明 参加华为od机试,一定要注意不要完全背…...

字符设备驱动基础(二)
目录 一、五种IO模型------读写外设数据的方式 二、阻塞与非阻塞 三、多路复用 3.1 应用层:三套接口select、poll、epoll 3.2 驱动层:实现poll函数 四、信号驱动 4.1 应用层:信号注册fcntl 4.2 驱动层:实现fasync函数 一、…...

看见统计——第三章 概率分布
看见统计——第三章 概率分布 参考 https://github.com/seeingtheory/Seeing-Theory中心极限定理 概率分布描述了随机变量取值的规律。 随机变量Random Variables 🔥 定义:将样本空间中的结果映射到实数的函数 XXX 称为随机变量(random variable)&a…...

【基于众包标注的语文教材句子难易度评估研究 论文精读】
基于众包标注的语文教材句子难易度评估研究 论文精读信息摘 要0 引言1 相关研究2 众包标注方法3 语料库构建3.1 数据收集3.1 基于五点量表的专家标注3.3 基于成对比较的众包标注4 特征及模型4.1 特征抽取4.2 模型与实验设计4.2.1 任务一:单句绝对难度评估4.2.2 任务二:句对相对…...

实例五:MATLAB APP design-APP登录界面的设计
一、APP 界面设计展示 注:在账号和密码提示框输入相应的账号和密码后,点击登录按钮,即可跳转到程序中设计的工作界面。 二、APP设计界面运行结果展示...

作用域和闭包:
1、LHS和RHS查询编译一段代码,需要js引擎和编译器(js引擎负责整个程序运行时所需的各种资源的调度,编译器只是js引擎的一部分,负责将JavaScript源码编译成机器能识别的机器指令,然后交给引擎运行)编译的过程…...

Vue常见面试题?
1、说说你对SPA单页面的理解,它的优缺点是什么? SPA(single-page application)仅在Web页面初始化时加载相应的HTML、JavaScript和CSS。一旦页面加载完成,SPA不会因为用户的操作而进行页面的重新加载或跳转;取而代之的是利用路由机…...

前端借助Canvas实现压缩图片两种方法
一、具体代码 1、利用canvas压缩图片方法一 // 第一种压缩图片方法(图片base64,图片类型,压缩比例,回调函数)// 图片类型是指 image/png、image/jpeg、image/webp(仅Chrome支持)// 该方法对以上三种图片类型都适用 压缩结果的图片base64与原类型相同// …...

2023年美赛C题Wordle预测问题二建模及Python代码详细讲解
更新时间:2023-2-19 相关链接 (1)2023年美赛C题Wordle预测问题一建模及Python代码详细讲解 (2)2023年美赛C题Wordle预测问题二建模及Python代码详细讲解 (3)2023年美赛C题Wordle预测问题三、四…...

【算法】双指针
作者:指针不指南吗 专栏:算法篇 🐾或许会很慢,但是不可以停下来🐾 文章目录1.双指针分类2.双指针思想3.双指针应用1.双指针分类 常见问题分类 (1) 对于一个序列,用两个指针维护一段区间, 比如快速排序。 …...

Flutter-Widget-学习笔记
Widget 是整个视图描述的基础。 参考:https://docs.flutter.dev/resources/architectural-overview Widget 到底是什么呢? Widget 是 Flutter 功能的抽象描述,是视图的配置信息,同样也是数据的映射,是 Flutter 开发框…...

easyExcel 写复杂表头
写模板 模板图片: 实体类(这里没有用Data 是因为Lombok和easyExcal的版本冲突,在导入读取的时候获取不到值) package cn.iocoder.yudao.module.project.controller.admin.goods.vo;import com.alibaba.excel.annotation.ExcelI…...

关于线程池的执行流程和拒绝策略
使用线程池的好处为: 降低资源消耗:减少线程的创建和销毁带来的性能开销。 提高响应速度:当任务来时可以直接使用,不用等待线程创建 可管理性: 进行统一的分配,监控,避免大量的线程间因互相抢…...

【李忍考研传】二、约定
因为收学生证用了好些时间,李忍把学生证都交给班长后,就赶忙跑去食堂。远远地,他就看到那个瘦小的身影立在食堂正门前,那是他们约定每天午餐集合的地方。 “你咋这么慢啊……” “害!帮班长收东西耽误了点时间&#…...

2023-2-19 刷题情况
修改两个元素的最小分数 题目描述 给你一个下标从 0 开始的整数数组 nums 。 nums 的 最小 得分是满足 0 < i < j < nums.length 的 |nums[i] - nums[j]| 的最小值。nums的 最大 得分是满足 0 < i < j < nums.length 的 |nums[i] - nums[j]| 的最大值。nu…...

LeetCode笔记:Weekly Contest 333
LeetCode笔记:Weekly Contest 333 1. 题目一 1. 解题思路2. 代码实现 2. 题目二 1. 解题思路2. 代码实现 3. 题目三 1. 解题思路2. 代码实现 4. 题目四 比赛链接:https://leetcode.com/contest/weekly-contest-333 1. 题目一 给出题目一的试题链接如下…...

元数据管理 1
1、关于元数据管理原则说法正确的是 (知识点: 三月份模拟题)A.确保员工了解如何访问和使用元数据。B.制定、实施和审核元数据标准,以简化元数据的集成和使用。C.创建反馈机制,以便数据使用者可以将错误或过时的元数据反馈给元数据管理团队。D.以上都对正…...

统计二进制中比特1的个数
快速统计比特1的数量int CountBitOnes(int32_t n) {int result 0;for(;n;result) {n & n-1;}return result; }原理很简单,n-1会将n中最靠近结尾的1减一,这样n&n-1,n中最靠近结尾的1就变成了0;假设n 0b xxxxxxxx100n - 1…...

第三方实现跑马灯和手写实现跑马灯
目录第三方实现跑马灯手写实现跑马灯手写实现跑马灯【整体代码】自己细心研究一下上述代码第三方实现跑马灯 https://vue3-marquee.vercel.app/guide.html#changes-from-v2https://evodiaaut.github.io/vue-marquee-text-component/ 手写实现跑马灯 CSS部分 <style>.m…...

React Native Cannot run program “node“问题
概述 前几天mac重装系统了,用Android studio重新构建React native项目时,报Cannot run program "node"错误。 电脑系统为macOS 12.6.3 (Monterey),M1 Pro芯片。设备信息如下图所示: 完整错误信息如下图所示ÿ…...

python基于vue微信小程序 房屋租赁出租系统
目录 1 绪论 1 1.1课题背景 1 1.2课题研究现状 1 1.3初步设计方法与实施方案 2 1.4本文研究内容 2 2 系统开发环境 4 2.1 2.2MyEclipse环境配置 4 2.3 B/S结构简介 4 2.4MySQL数据库 5 2. 3 系统分析 6 3.1系统可行性分析 6 3.1.1经济可行性 6 3.1.2技术可行性 6 3.1.3运行可行…...

ThreadPoolExecutor管理异步线程笔记
为什么使用线程池? 线程的创建和销毁都需要不小的系统开销,不加以控制管理容易发生OOM错误。避免线程并发抢占系统资源导致系统阻塞。具备一定的线程管理能力(数量、存活时间,任务管理) new ThreadPoolExecutor(int …...