Spring Boot 3 集成 Spring Security(3)数据管理

宁睿  论坛元老 | 2024-11-29 02:02:28 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1084|帖子 1084|积分 3262

在前面的文章中我们介绍了 《Spring Boot 3 集成 Spring Security(1)认证》和 《Spring Boot 3 集成 Spring Security(2)授权》,这篇博客将介绍怎样在 Spring Boot 3 项目中,整合 Spring Security 和 MyBatis-Plus ,轻松实现基于数据库的用户访问控制、权限管理。
准备工作

新建项目

springboot3-security-mysql-example 引入依靠

引入MyBatis-Plus依靠

MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的根本上只做增强不做改变,为简化开发、进步服从而生。
   留意事项
  版本 3.5.9+ 开始修改为可选依靠,具体查看下文 maven bom 部门。
  1.   <mybatisplus.version>3.5.9</mybatisplus.version>
复制代码
  1.     <!-- MyBatis-Plus https://baomidou.com-->
  2.     <dependency>
  3.         <groupId>com.baomidou</groupId>
  4.         <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
  5.     </dependency>
  6.     <dependency>
  7.         <groupId>com.baomidou</groupId>
  8.         <artifactId>mybatis-plus-jsqlparser</artifactId>
  9.     </dependency>
复制代码
  1.     <dependencyManagement>
  2.         <dependencies>
  3.             <dependency>
  4.                 <groupId>com.baomidou</groupId>
  5.                 <artifactId>mybatis-plus-bom</artifactId>
  6.                 <version>${mybatisplus.version}</version>
  7.                 <type>pom</type>
  8.                 <scope>import</scope>
  9.             </dependency>
  10.         </dependencies>
  11.     </dependencyManagement>
复制代码
创建表布局

这里我们定义三张表,来实现用户角色权限的操作

  1. -- 用户表
  2. CREATE TABLE `sys_user` (
  3.         `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
  4.         `username` VARCHAR ( 64 ) DEFAULT NULL COMMENT '用户名',
  5.         `password` VARCHAR ( 64 ) DEFAULT NULL COMMENT '密码',
  6.         `sex` CHAR ( 1 ) DEFAULT '0' COMMENT '性别 0 男 1 女 2 未知',
  7.         `nick_name` VARCHAR ( 64 ) DEFAULT NULL COMMENT '昵称',
  8.         `status` CHAR ( 1 ) DEFAULT '1' COMMENT '账号状态 0 禁用 1 启用',
  9.         `valid` INT DEFAULT '1' COMMENT '有效状态 0 无效 1 有效',
  10. PRIMARY KEY ( `id` )
  11. ) ENGINE = INNODB COMMENT = '用户';
  12. INSERT INTO `sys_user` (`id`, `username`, `password`, `sex`, `nick_name`, `status`, `valid`) VALUES (1, 'admin', '$2a$10$xZdonloiiL6YfoLZv6mrJuvxtD238uHPIKkVDpQKBuZxzMDpTf8uK', '0', '管理员张三', '1', 1);
  13. INSERT INTO `sys_user` (`id`, `username`, `password`, `sex`, `nick_name`, `status`, `valid`) VALUES (2, 'user', '$2a$10$evM9SfvuN/E.ykWWOf6b3eTltPvuc6XjwW/qIhagSjlsTfi9l26Ba', '0', '用户李四', '1', 1);
  14. -- 角色表
  15. CREATE TABLE `sys_role` (
  16.         `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
  17.         `name` VARCHAR ( 64 ) DEFAULT NULL COMMENT '角色名',
  18.         `code` VARCHAR ( 64 ) DEFAULT NULL COMMENT '密码',
  19.         `status` CHAR ( 1 ) DEFAULT '1' COMMENT '状态 0 禁用 1 启用',
  20.         `valid` INT DEFAULT '1' COMMENT '有效状态 0 无效 1 有效',
  21. PRIMARY KEY ( `id` )
  22. ) ENGINE = INNODB COMMENT = '角色';
  23. INSERT INTO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (1, '管理员', 'ROOT', '1', 1);
  24. INSERT INTO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (2, '普通用户', 'USER', '1', 1);
  25. -- 用户角色关系表
  26. CREATE TABLE `sys_user_role` (
  27.         `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
  28.   `user_id` bigint DEFAULT NULL COMMENT '用户ID',
  29.   `role_id` bigint DEFAULT NULL COMMENT '角色ID',
  30.   PRIMARY KEY (`id`) USING BTREE
  31. ) ENGINE = INNODB COMMENT = '用户角色关系表';
  32. INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (1, 1, 1);
  33. INSERT INTO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (2, 2, 2);
复制代码
  默认设置账户密码123456,在数据库中使用加密后的密码,关于密码加密,可以使用下面的测试方法。
  1.     public static void main(String[] args) {
  2.         BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
  3.         String result = encoder.encode("123456");
  4.         // 输出加密后的密码
  5.         System.out.println(result);
  6.         // 对比加密后的密码和明文密码
  7.         System.out.println(encoder.matches("123456", result));
  8.     }
复制代码
生成根本代码

这里我用了代码生成器插件,以进步生产服从,想具体了解,可以去官网上搭建使用。这里就不多说啦

当地代码勾选,使用 mybatis-plus 3


   准备工作到这里基本上就可以了,接下来开始实现从数据库中读取用户角色权限
  逻辑实现

application.yml配置

  1. spring:
  2.   thymeleaf:
  3.     # 设置Thymeleaf模板文件的前缀位置(默认是`src/main/resources/templates`)
  4.     prefix: classpath:/templates/
  5.     # 设置模板文件的后缀(默认是`.html`)
  6.     suffix: .html
  7.     # 设置模板模式(默认是HTML5,Thymeleaf 3中为`HTML`)
  8.     mode: HTML
  9.     # 开启模板缓存(开发时建议关闭,生产时开启)
  10.     cache: false
  11.   datasource:
  12.     driver-class-name: com.mysql.cj.jdbc.Driver
  13.     url: jdbc:mysql://127.0.0.1:3306/security_data?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
  14.     username: root
  15.     password: root
  16. #mybatis
  17. mybatis-plus:
  18.   mapper-locations: classpath*:/mapper/**/*.xml
  19.   #实体扫描,多个package用逗号或者分号分隔
  20.   typeAliasesPackage: cn.harry.*.domain
  21.   global-config:
  22.     #数据库相关配置
  23.     db-config:
  24.       #主键类型  AUTO:"数据库ID自增", INPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
  25.       id-type: AUTO
  26.       # 逻辑删除全局属性名(驼峰和下划线都支持)
  27.       logic-delete-field: valid
  28.       logic-delete-value: 0 # 逻辑已删除值(默认为 1)
  29.       logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)
  30.     banner: false
  31.   #原生配置
  32.   configuration:
  33.     map-underscore-to-camel-case: true
  34.     cache-enabled: false
  35.     call-setters-on-nulls: true
  36.     jdbc-type-for-null: 'null'
  37.     # 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
  38.     log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
复制代码
SecurityConfig 配置

要使用 Spring Security 进行用户认证,我们需要配置 SecurityConfig,并实现自定义的 UserDetailsService 来与数据库中的用户信息进行集成。
  1. /**
  2. * @author harry
  3. * @公众号 Harry技术
  4. */
  5. @Configuration
  6. @EnableWebSecurity
  7. @EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制
  8. @RequiredArgsConstructor
  9. public class SecurityConfig {
  10.    
  11.     // 通过构造函数注入自定义UserDetailsService
  12.     private final UserDetailsService userDetailsService;
  13.     @Bean
  14.     public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  15.         http
  16.                 .authorizeHttpRequests(auth -> auth
  17.                         // 公开访问
  18.                         .requestMatchers("/").permitAll()
  19.                         // 其他接口需认证
  20.                         .anyRequest().authenticated()
  21.                 )
  22.                 .userDetailsService(userDetailsService)
  23. //                .exceptionHandling(exception -> exception
  24. //                        .authenticationEntryPoint(restAuthenticationEntryPoint)
  25. //                        .accessDeniedHandler(restfulAccessDeniedHandler)
  26. //                )
  27.                 // 开启基于表单的登录
  28.                 .formLogin(Customizer.withDefaults())
  29. //                // 开启注销功能
  30. //                .logout(Customizer.withDefaults())
  31. //                // 开启 HTTP Basic 认证
  32. //                .httpBasic(Customizer.withDefaults())
  33. //                // 开启 CSRF 防护
  34. //                .csrf(Customizer.withDefaults())
  35. //                // 开启跨域资源共享
  36. //                .cors(Customizer.withDefaults())
  37.         ;
  38.         return http.build();
  39.     }
  40.     @Bean
  41.     public PasswordEncoder passwordEncoder() {
  42.         // 使用 BCrypt 进行密码加密
  43.         return new BCryptPasswordEncoder();
  44.     }
复制代码
自定义 UserDetailsService

想从数据库加载用户信息,就需要创建一个自定义的 UserDetailsService 实现类,它的重要作用:
用户认证:
UserDetailsService 负责从数据源(如数据库、LDAP等)中加载用户特定的安全信息,包括用户名、密码和权限(角色)。
Spring Security 使用 UserDetailsService 来验证用户提供的根据是否精确。
用户授权:
加载用户的权限信息,以便在认证乐成后进行授权检查。
权限信息通常包括用户的角色(如 ROLE_ADMIN, ROLE_USER 等),这些角色用于控制用户可以访问的资源和操作。
  1. /**
  2. * 系统用户认证  service
  3. *
  4. * @author harry
  5. * @公众号 Harry技术
  6. */
  7. @Slf4j
  8. @Service
  9. @RequiredArgsConstructor
  10. public class UserDetailsServiceImpl implements UserDetailsService {
  11.     private final SysUserMapper sysUserMapper;
  12.     private final SysUserRoleMapper sysUserRoleMapper;
  13.     @Override
  14.     public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  15.         try {
  16.             // 获取登录用户信息
  17.             SysUser user = sysUserMapper.selectByUsername(username);
  18.             // 用户不存在、用户停用 等校验 TODO
  19.             Long useId = user.getId();
  20.             // 获取角色
  21.             Set<String> roles = sysUserRoleMapper.listRoleKeyByUserId(useId);
  22.             return new SysUserDetails(user, roles, username);
  23.         } catch (Exception e) {
  24.             log.error("loadUserByUsername error", e);
  25.         }
  26.         return null;
  27.     }
  28. }
复制代码
我们根据数据库中的用户信息加载用户,并将角色转换为 Spring Security 能识别的格式。我们写一个SysUserDetails类来实现自定义Spring Security 用户对象。
  1. /**
  2. * 自定义 Spring Security 用户对象
  3. *
  4. * @author harry
  5. * @公众号 Harry技术
  6. */
  7. @Data
  8. @NoArgsConstructor
  9. public class SysUserDetails implements UserDetails {
  10.     private String username;
  11.     private SysUser sysUser;
  12.     private Collection<SimpleGrantedAuthority> authorities;
  13.     public SysUserDetails(SysUser user, Set<String> roles, String username) {
  14.         this.sysUser = user;
  15.         Set<SimpleGrantedAuthority> authorities;
  16.         if (CollectionUtil.isNotEmpty(roles)) {
  17.             // 标识角色 前面加上 ROLE_
  18.             authorities = roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toSet());
  19.         } else {
  20.             authorities = Collections.emptySet();
  21.         }
  22.         this.authorities = authorities;
  23.         this.username = username;
  24.     }
  25.     @Override
  26.     public Collection<? extends GrantedAuthority> getAuthorities() {
  27.         // 返回当前用户的权限
  28.         return authorities;
  29.     }
  30.     @Override
  31.     public String getPassword() {
  32.         return sysUser.getPassword();
  33.     }
  34.     @Override
  35.     public String getUsername() {
  36.         return this.username;
  37.     }
  38.     /**
  39.      * 是否可用 ,禁用的用户不能身份验证
  40.      *
  41.      * @return 是否可用
  42.      */
  43.     @Override
  44.     public boolean isEnabled() {
  45.         return StatusEnums.ENABLE.getKey().equals(sysUser.getStatus());
  46.     }
  47. }
复制代码
创建测试



  • 页面
  1.   <!DOCTYPE html>
  2.   <html lang="en">
  3.   <head>
  4.       <meta charset="UTF-8">
  5.       <title>Title</title>
  6.       <style>
  7.           .content {
  8.               width: 800px;
  9.               height: 800px;
  10.               text-align: center;
  11.               line-height: 100px;
  12.               font-size: 20px;
  13.               flex: 1;
  14.               flex-direction: column;
  15.               display: flex;
  16.               justify-content: center;
  17.               align-items: center;
  18.           }
  19.       </style>
  20.   </head>
  21.   <body>
  22.   <div class="content">
  23.       <!--去登陆 -->
  24.       <a href="/login">去登陆</a>
  25.   
  26.       <!-- admin/info 接口 -->
  27.       <a href="/admin/info">访问 admin/info 接口</a>
  28.   
  29.       <!-- 去首页 -->
  30.       <a href="/user/info">访问 user/info 接口</a>
  31.   
  32.       <!--退出-->
  33.       <a href="/logout">退出</a>
  34.   
  35.   </div>
  36.   </body>
  37.   </html>
复制代码


  • 接口
    改写接口admin/info,并配置 @PreAuthorize("hasRole('ROOT')")只有 ADMIN 角色才气访问
  1.   
  2.   /**
  3.    * @author harry
  4.    * @公众号 Harry技术
  5.    * Spring Boot 3 集成 Spring Security(2) 授权: https://mp.weixin.qq.com/s/HzzcYIQLnch_7r7wdUarew
  6.    */
  7.   @Slf4j
  8.   @RestController
  9.   public class AdminController {
  10.   
  11.   
  12.       @GetMapping("/admin/info")
  13.       @PreAuthorize("hasRole('ROOT')")  // 只有 ADMIN 角色才能访问
  14.       public SysUserDetails adminInfo() {
  15.           // 获取当前登录的用户信息
  16.           SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  17.           log.info("当前登录的用户信息:{}", user.toString());
  18.           return user;
  19.       }
  20.   }
  21.   
复制代码
改写接口user/info,并配置 @PreAuthorize("hasRole('USER')")只有 USER 角色才气访问
  1.       
  2.   
  3.   @Slf4j
  4.   @RestController
  5.   public class UserController {
  6.   
  7.       @GetMapping("/user/info")
  8.       @PreAuthorize("hasRole('USER')")  // 只有 user 角色才能访问
  9.       public SysUserDetails userInfo() {
  10.           // 获取当前登录的用户信息
  11.           SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  12.           log.info("当前登录的用户信息:{}", user.toString());
  13.           return user;
  14.       }
  15.   }
  16.   
复制代码
启动测试



  • 1.登录admin账户
访问 admin/info 接口

访问 user/info 接口



  • 2.登录user用户
访问 admin/info 接口

访问 user/info 接口

通过上面的测试用例,通过定义用户和角色实体、实现自定义的 UserDetailsService,实现了数据库驱动的用户认证和基于角色的授权机制。这种结合方式不仅在安全性上提供了极大的机动性,也让数据管理变得更加简洁高效。
关注我,在后续的文章中,我们进一步探讨如果使用JWT、OAuth2 等机制、使用Redis作为缓存来强化认证与授权的实现。
示例源码:关注公众号“Harry技术”,回复 security 获取源码地址。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

宁睿

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表