当前位置: 首页 > news >正文

SpringSecurity的权限校验详解说明(附完整代码)

说明

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的安全认证的详解说明(附完整代码) &#xff08;https://blog.csdn.net/qq_51076413/article/details/129102660&#xff09;的讲解&#xff0c;如果不了解SpringSecurity是怎么认证&#xff0c;请先看下【SpringSecurity的安…...

Java-集合(5)

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

研制过程评审活动(四)设计定型阶段

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

【Linux】进程替换

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

LeetCode171-Excel表列序号(进制转换问题)

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

React SSR

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

如何系统地优化页面性能

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

Vulnhub 渗透练习(八)—— THE ETHER: EVILSCIENCE

环境搭建 环境下载 靶机和攻击机网络适配都选 NAT 即可。 信息收集 主机扫描 两个端口&#xff0c;22 和 80&#xff0c;且 apache httpd 2.4.0~2.4.29 存在换行解析漏洞。 Apache HTTPD是一款HTTP服务器&#xff0c;它可以通过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 应用层&#xff1a;三套接口select、poll、epoll 3.2 驱动层&#xff1a;实现poll函数 四、信号驱动 4.1 应用层&#xff1a;信号注册fcntl 4.2 驱动层&#xff1a;实现fasync函数 一、…...

看见统计——第三章 概率分布

看见统计——第三章 概率分布 参考 https://github.com/seeingtheory/Seeing-Theory中心极限定理 概率分布描述了随机变量取值的规律。 随机变量Random Variables &#x1f525; 定义&#xff1a;将样本空间中的结果映射到实数的函数 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查询编译一段代码&#xff0c;需要js引擎和编译器&#xff08;js引擎负责整个程序运行时所需的各种资源的调度&#xff0c;编译器只是js引擎的一部分&#xff0c;负责将JavaScript源码编译成机器能识别的机器指令&#xff0c;然后交给引擎运行&#xff09;编译的过程…...

Vue常见面试题?

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

前端借助Canvas实现压缩图片两种方法

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

2023年美赛C题Wordle预测问题二建模及Python代码详细讲解

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

【算法】双指针

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

Flutter-Widget-学习笔记

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

easyExcel 写复杂表头

写模板 模板图片&#xff1a; 实体类&#xff08;这里没有用Data 是因为Lombok和easyExcal的版本冲突&#xff0c;在导入读取的时候获取不到值&#xff09; package cn.iocoder.yudao.module.project.controller.admin.goods.vo;import com.alibaba.excel.annotation.ExcelI…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

visual studio 2022更改主题为深色

visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中&#xff0c;选择 环境 -> 常规 &#xff0c;将其中的颜色主题改成深色 点击确定&#xff0c;更改完成...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

Linux离线(zip方式)安装docker

目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1&#xff1a;修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本&#xff1a;CentOS 7 64位 内核版本&#xff1a;3.10.0 相关命令&#xff1a; uname -rcat /etc/os-rele…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

接口 RESTful 中的超媒体:REST 架构的灵魂驱动

在 RESTful 架构中&#xff0c;** 超媒体&#xff08;Hypermedia&#xff09;** 是一个核心概念&#xff0c;它体现了 REST 的 “表述性状态转移&#xff08;Representational State Transfer&#xff09;” 的本质&#xff0c;也是区分 “真 RESTful API” 与 “伪 RESTful AP…...