目录引入依赖及原理简述 基础自定义配置 前后端分离及登录结果管理 角色权限管理基础 
从此处开始,为新的原创内容,相关数据结构代码换了一套新的,与之前的代码关系不大了。
六、RBAC 结构实现 自定义响应式登录与 JWT 配置 集成 Redis 
 
Spring Security(二)多用户维护配置 
博主前言:本以为这个就是代替传统 jwt 的插件,没想到复杂程度如此之高。Spring Security 本身是个高度自定义化的组件,必须花时间重点学习一下。以下为个人配置学习的流程,从零到权限管理、redis嵌入等步骤。尚硅谷Java项目SpringSecurity+OAuth2权限管理实战教程 
 
 在实际开发需求中,我们不可能只有一个用户需要维护,好在 Spring Security 有一套完整的流程支持这一操作。
一、基于内存的用户认证 
配置类 
 
 先来见见 Spring Security 的Config类:
1 2 3 4 @Configuration @EnableWebSecurity  public  class  SecurityConfig  {} 
实际上,Spring boot 项目不需要添加@EnableWebSecurity,因为其autoconfig包会默认启用依赖的相关配置。
 
基于内存的用户信息管理器 
 
 在Config类内创建基于内存的用户信息管理器Bean:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Bean public  UserDetailsService userDetailsService ()  {         var  manager  =  new  InMemoryUserDetailsManager ();               manager.createUser(User.withDefaultPasswordEncoder()             .username("user" )             .password("password" )             .roles("USER" )             .build());     return  manager; } 
 UserDetailsService的作用:储存并维护用户认证信息的服务类,其对应的用户认证信息类为UserDetails。
 UserDetails的作用:包含用户名、密码、权限等内容。
UserDetailsService与UserDetails均为抽象接口,前者为不同的维护方式,后者则根据实际情况具体实现。
上述代码中的User即为 Spring Security 默认提供的UserDetails实现。
PasswordEncoder为密码的加密编码器,该对象使用默认编码(已弃用),加密编码器可以全局指定 Spring Security 自带的更好的编码器(强哈希BCryptPasswordEncoder()),亦可以自定义。
 
 此时,默认配置失效,因为我们已经重写了UserDetailsService,默认配置不再受理。
二、基于数据库的数据源的用户认证 
接下来的 SQL 部分基于 Mysql 和 Mybatis-Plus,使用了 Lombok 辅助对象编写,相关依赖及功能这里不再赘述。
Mybatis-Plus 是 Mybatis 的扩展,不对原生 Mybatis 有影响。
 
创建数据表 
 
表创建好之后,记得随便放进去一些测试数据
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 CREATE TABLE  `user ` (  `id` int  NOT NULL ,   `username` varchar (50 ) COLLATE  utf8mb4_0900_as_ci DEFAULT  NULL ,   `password` varchar (500 ) COLLATE  utf8mb4_0900_as_ci DEFAULT  NULL ,   `enable` tinyint NOT NULL  DEFAULT  '1' ,   PRIMARY KEY  (`id`),   UNIQUE  KEY `user_username_unidex` (`username`) ) ENGINE= InnoDB DEFAULT  CHARSET= utf8mb4 COLLATE = utf8mb4_0900_as_ci; INSERT INTO  `user ` ( `id`, `username`, `password`, `enabled`) VALUES ('0' , 'admin' , '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW' , TRUE ), ('1' , 'Helen' , '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW' , TRUE ), ('2' , 'Tom' , '{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW' , TRUE ); 
准备对应数据类、持久层类、服务层类和控制层类 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 @Data public  class  User  {    @TableId(type = IdType.ASSIGN_ID)      private  Integer id;     private  String username;     private  String password;     private  Boolean enable; } @Mapper public  interface  UserMapper  extends  BaseMapper <User> {} public  interface  IUserService  extends  IService <User> {} @Service public  class  UserServiceImpl  extends  ServiceImpl <UserMapper, User> implements  IUserService  {} @RestController @RequestMapping @RequiredArgsConstructor public  class  UserController  {    private  final  IUserService userService;     @GetMapping("/list")      public  List<User> getList ()  {         return  userService.list();     } } 
自定义UserDetailsService 
 
 基于内存的用户认证中,我们已经了解到UserDetailsService是维护用户认证的服务类。在基于内存的方式,有 Spring Security 提供的封装类,我们可以通过Bean注入来装配。但数据表的结构复杂多变,对于基于数据库的数据认证,我们需要自己编写一个UserDetailsService类来适配我们的数据表。
别忘了注释掉SecurityConfig中基于内存的用户信息管理器Bean
 
 对于我自己来说,我的做法更加大胆:将用户服务层接口继承UserDetailsService。原本用户逻辑的维护就是用户服务类的任务之一,将UserDetailsService重新整合回用户服务在设计上没有问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public  interface  IUserService  extends  IService <User>, UserDetailsService {} @Service public  class  UserServiceImpl  extends  ServiceImpl <UserMapper, User> implements  IUserService  {    @Override      public  UserDetails loadUserByUsername (String username)  throws  UsernameNotFoundException {         var  userLQW  =  new  LambdaQueryWrapper <User>()                 .eq(User::getUsername, username);         var  user  =  this .getOne(userLQW);         if  (user == null )             throw  new  UsernameNotFoundException ("用户不存在:"  + username);         return  user;     } } 
 同时,我们需要注意一件事:UserDetailsService管理的是UserDetails,而非我们自定义的User对象。所以,我们要将User继承UserDetails接口,从而交给UserDetailsService管理(经过上面的操作,实际管理的是用户服务层接口)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 @Data public  class  User  implements  UserDetails  {    @TableId(type = IdType.ASSIGN_ID)      private  Integer id;     private  String username;     private  String password;     private  Boolean enable;     @Override      public  Collection<? extends  GrantedAuthority > getAuthorities() {                  return  List.of();     }     @Override      public  boolean  isEnabled ()  {         return  enable;     }     @Override      public  boolean  isAccountNonExpired ()  {         return  UserDetails.super .isAccountNonExpired();     }     @Override      public  boolean  isAccountNonLocked ()  {         return  UserDetails.super .isAccountNonLocked();     }     @Override      public  boolean  isCredentialsNonExpired ()  {         return  UserDetails.super .isCredentialsNonExpired();     } } 
 至此,基于数据库的数据源的用户认证配置完成。