目录 一、引入依赖及原理简述 二、多用户维护配置 <— 你在这里 ( •̀ ω •́ )y 三、基础自定义配置 四、前后端分离及登录结果管理 五、角色权限管理基础
从此处开始,为新的原创内容,相关数据结构代码换了一套新的,与之前的代码关系不大了。 建议新建一个项目,将配置文件复制过来,然后按照步骤走。
六、RBAC 结构实现 七、自定义响应式登录与 JWT 配置 八、集成 Redis
Spring Security(二)多用户维护配置
博主前言:本以为这个就是代替传统 jwt 的插件,没想到复杂程度如此之高。Spring Security 本身是个高度自定义化的组件,必须花时间重点学习一下。以下为个人配置学习的流程,从零到权限管理、redis嵌入等步骤。 本文基于尚硅谷的 Spring Security 教程学习,文章与原教程有不小出入,仅供参考。 B站视频链接:尚硅谷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(); } }
至此,基于数据库的数据源的用户认证配置完成。