目录 一、引入依赖及原理简述 多用户维护配置 基础自定义配置 前后端分离及登录结果管理 角色权限管理基础 
从此处开始,为新的原创内容,相关数据结构代码换了一套新的,与之前的代码关系不大了。
六、RBAC 结构实现 <— 你在这里 ( •̀ ω •́ )y自定义响应式登录与 JWT 配置 集成 Redis 
 
Spring Security(六)RBAC 结构实现 
博主前言:本以为这个就是代替传统 jwt 的插件,没想到复杂程度如此之高。Spring Security 本身是个高度自定义化的组件,必须花时间重点学习一下。以下为个人配置学习的流程,从零到权限管理、redis嵌入等步骤。尚硅谷Java项目SpringSecurity+OAuth2权限管理实战教程 
 
RBAC(用户 - 角色 - 权限 - 资源)是目前广泛应用的权限结构,该结构能够动态管理权限,一般为三个对象模型和两个链接模型:
如图,用户可以有多个角色身份,角色可以被分配多种权限。
接下来我们要实现这个结构。
[!WARNING]
特殊注意,此角色非彼角色:
虽然我们设计了【角色】这个模型,但是在代码层面上,【角色】与代码的耦合度非常高——管理端和用户端的授权注解都是硬编码。
实际上,【角色】表为权限的打包集合,用于为用户分配权限及划分类别。
所以,若为单端系统,代码层面无需配置角色;若为双端乃至多端系统,代码层面每一端配置一个角色。
 
一、数据表实现 一共五个表,三个主表两个链接表。
用户表user 
 
列名 
数据类型 
描述 
 
 
id 
int 
用户ID 
 
username 
varchar 
用户名 
 
password 
varchar 
密码 
 
status 
tinyint 
状态(启用/禁用) 
 
… 
… 
… 
 
角色表role 
 
列名 
数据类型 
描述 
 
 
id 
int 
角色ID 
 
name 
varchar 
角色名称 
 
type 
varchar 
所属角色组 
 
用户-角色链接表lk_user_role 
 
列名 
数据类型 
描述 
 
 
id 
int 
用户角色关联ID 
 
user_id 
int 
用户ID 
 
role_id 
int 
角色ID 
 
权限表permission 
 
列名 
数据类型 
描述 
 
 
id 
int 
权限ID 
 
name 
varchar 
权限名称 
 
value 
varchar 
权限值 
 
角色-权限链接表lk_role_permission 
 
列名 
数据类型 
描述 
 
 
id 
int 
用户角色关联ID 
 
role_id 
int 
角色ID 
 
permission_id 
int 
权限ID 
 
建表语句和测试数据:
[!NOTE]
构建对应的Mybatis-Plus结构这里不再赘述。
密码均为 password
 
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 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 CREATE TABLE  user  (    id INT  AUTO_INCREMENT PRIMARY KEY ,       username VARCHAR (255 ) NOT NULL ,           password VARCHAR (255 ) NOT NULL ,           status TINYINT(1 ) DEFAULT  1 ,                   UNIQUE  (username)                     ); CREATE TABLE  role (    id INT  AUTO_INCREMENT PRIMARY KEY ,         name VARCHAR (255 ) NOT NULL ,                 type VARCHAR (16 ) NOT NULL           ); CREATE TABLE  lk_user_role (    id INT  AUTO_INCREMENT PRIMARY KEY ,        user_id INT  NOT NULL ,                      role_id INT  NOT NULL ,                      FOREIGN KEY  (user_id) REFERENCES  user (id) ON  DELETE  CASCADE,        FOREIGN KEY  (role_id) REFERENCES  role(id) ON  DELETE  CASCADE     ); CREATE TABLE  permission (    id INT  AUTO_INCREMENT PRIMARY KEY ,        name VARCHAR (255 ) NOT NULL ,                value  VARCHAR (32 ) NOT NULL               ); CREATE TABLE  lk_role_permission (    id INT  AUTO_INCREMENT PRIMARY KEY ,        role_id INT  NOT NULL ,                      permission_id INT  NOT NULL ,                FOREIGN KEY  (role_id) REFERENCES  role(id) ON  DELETE  CASCADE,        FOREIGN KEY  (permission_id) REFERENCES  permission(id) ON  DELETE  CASCADE   ); INSERT INTO  `lk_role_permission` VALUES  (1 , 1 , 1 );INSERT INTO  `lk_role_permission` VALUES  (2 , 1 , 2 );INSERT INTO  `lk_role_permission` VALUES  (3 , 2 , 2 );INSERT INTO  `lk_user_role` VALUES  (4 , 1 , 1 );INSERT INTO  `lk_user_role` VALUES  (5 , 2 , 2 );INSERT INTO  `permission` VALUES  (1 , '查询用户列表' , 'user_list' );INSERT INTO  `permission` VALUES  (2 , '查询自己' , 'user_myself' );INSERT INTO  `role` VALUES  (1 , '管理员' , 'admin' );INSERT INTO  `role` VALUES  (2 , '普通用户' , 'user' );INSERT INTO  `user ` VALUES  (1 , 'admin' , '$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW' , 1 );INSERT INTO  `user ` VALUES  (2 , 'user' , '$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW' , 1 );
二、重写对应逻辑  重写User结构,适配UserDetails:
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 37 38 39 40 41 42 43 44 45 46 @Data public  class  User  implements  UserDetails  {    @TableId(type = IdType.AUTO)      private  Integer id;     private  String username;     private  String password;     private  Integer status;          @TableField(exist = false)      private  List<GrantedAuthority> authorities;     @Override      public  Collection<? extends  GrantedAuthority > getAuthorities() {         return  authorities;     }     @Override      public  String getUsername ()  {         return  username;     }     @Override      public  String getPassword ()  {         return  password;     }     @Override      public  boolean  isEnabled ()  {         return  status == 1 ;     }     @Override      public  boolean  isAccountNonExpired ()  {         return  UserDetails.super .isAccountNonExpired();     }     @Override      public  boolean  isAccountNonLocked ()  {         return  UserDetails.super .isAccountNonLocked();     }     @Override      public  boolean  isCredentialsNonExpired ()  {         return  UserDetails.super .isCredentialsNonExpired();     } } 
重写UserDetailService中的用户维护校验方法:
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 @Service @RequiredArgsConstructor public  class  UserServiceImpl  extends  ServiceImpl <UserMapper, User> implements  UserService  {    private  final  PasswordEncoder passwordEncoder;     private  final  RoleService roleService;     private  final  PermissionService permissionService;     @Override      public  UserDetails loadUserByUsername (String username)  throws  UsernameNotFoundException {                  var  user  =  getByUsername(username);         if  (user == null )             throw  new  UsernameNotFoundException ("用户不存在:"  + username);         var  authorityList  =  new  ArrayList <GrantedAuthority>();                  List<Role> roleList = roleService.getByUserId(user.getId());         roleList.forEach(role -> {                          authorityList.add(() -> "ROLE_"  + role.getType());                          List<Permission> permissionList = permissionService.getByRoleId(role.getId());             permissionList.forEach(permission -> authorityList.add(permission::getValue));         });                  user.setAuthorities(authorityList);         return  user;     }     private  User getByUsername (String username)  {         return  this .getOne(new  LambdaQueryWrapper <User>()                 .eq(User::getUsername, username));     } } 
对应的链接键查找方法:
[!NOTE]
读者可以参考这篇文章来更好的配置链接键:代码层多对多结构的通用处理 
 
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 37 @Service @RequiredArgsConstructor public  class  RoleServiceImpl  extends  ServiceImpl <RoleMapper, Role> implements  RoleService  {    private  final  LkUserRoleService lkUserRoleService;     @Override      public  List<Role> getByUserId (Integer id)  {                  var  lkLQW  =  new  LambdaQueryWrapper <LkUserRole>()                 .eq(LkUserRole::getUserId, id);         var  lkList  =  lkUserRoleService.list(lkLQW);                  var  resList  =  new  ArrayList <Role>();         lkList.forEach(lk -> resList.add(this .getById(lk.getRoleId())));         return  resList;     } } @Service @RequiredArgsConstructor public  class  PermissionServiceImpl  extends  ServiceImpl <PermissionMapper, Permission> implements  PermissionService  {    private  final  LkRolePermissionService lkPermissionService;     @Override      public  List<Permission> getByRoleId (Integer id)  {                  var  lkLQW  =  new  LambdaQueryWrapper <LkRolePermission>()                 .eq(LkRolePermission::getRoleId, id);         var  lkList  =  lkPermissionService.list(lkLQW);                  var  resList  =  new  ArrayList <Permission>();         lkList.forEach(lk -> resList.add(this .getById(lk.getPermissionId())));         return  resList;     } } 
三、测试多端权限 配置Config类:
1 2 3 4 5 6 7 http.authorizeHttpRequests(authorize -> authorize                  .requestMatchers("/admin/**" ).hasRole("admin" )         .requestMatchers("/user/**" ).hasRole("user" )                  .anyRequest().authenticated()) 
编写用户列表接口:
1 2 3 4 5 6 7 8 9 10 11 @RestController @RequestMapping("/user") @RequiredArgsConstructor public  class  UserController  {    private  final  UserService userService;     @GetMapping("/list")      public  Result<List<User>> getList ()  {         return  Result.success(userService.list());     } } 
登录并访问,发现仅user可以获取列表,admin无权限。
四、测试接口权限 配置Config类:
添加@EnableMethodSecurity注解 
注释【配置不同终端访问权限】 
 
修改接口:
1 2 3 4 5 6 7 8 9 10 11 12 @RestController @RequestMapping("/user") @RequiredArgsConstructor public  class  UserController  {    private  final  UserService userService;     @GetMapping("/list")      @PreAuthorize("hasAuthority('user_list')")      public  Result<List<User>> getList ()  {         return  Result.success(userService.list());     } } 
再次登录并访问,这一次仅admin可以获取列表,user无权限。