🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志
🎐 个人CSND主页——Micro麦可乐的博客
🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战
🌺《RabbitMQ》专栏主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战
🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解
💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程
🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整
💋《Spring Security》专栏中我们将逐步深入Spring Security的各个技术细节,带你从入门到精通,全面掌握这一安全技术
如果文章能够给大家带来一定的帮助!欢迎关注、评论互动~
最新Spring Security实战教程(五)基于数据库的动态用户认证传统RBAC角色模型实战开发
回顾链接:
最新Spring Security实战教程(一)初识Spring Security安全框架
最新Spring Security实战教程(二)表单登录定制到处理逻辑的深度改造
最新Spring Security实战教程(三)Spring Security 的底层原理解析
最新Spring Security实战教程(四)基于内存的用户认证
1. 前言
在上一章节中,我们讲解了 Spring Security
基于内存的用户认证,也提到了实际开发生产中,更多使用的还是基于数据库的动态用户认证 ,因为在企业应用中,用户、角色、权限管理通常都存储在数据库中。
本章节博主带着大家以 MySQL
数据库为例,从用户(sys_user)、角色(sys_role)用户角色(sys_user_role)表出发,演示如何使用 Spring Security
动态加载用户信息、角色,实现基于数据库的认证
2. 数据库表结构说明
本文示例基于你整理的 MySQL 表结构,其中主要表结构如下:大家可以根据自己的业务需求进行扩展
sys_user
:存储用户信息,字段包括 user_id、login_name、password等sys_role
:存储角色信息,字段包括 role_id、role_name(角色名称)、role_key(角色标识)等sys_user_role
:关联用户和角色中间表
整体数据库表结构如下:
以下是为大家整理好的建表语句, 数据库中的用户表:用户名和密码一致
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
`role_id` bigint NOT NULL AUTO_INCREMENT COMMENT '角色ID',
`role_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色名称',
`role_key` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色权限字符串',
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='角色信息表';
-- ----------------------------
-- Records of sys_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_role` (`role_id`, `role_name`, `role_key`) VALUES (1, '管理员', 'ADMIN');
INSERT INTO `sys_role` (`role_id`, `role_name`, `role_key`) VALUES (2, '普通用户', 'USER');
INSERT INTO `sys_role` (`role_id`, `role_name`, `role_key`) VALUES (3, '开发者', 'DEVELOPERS');
COMMIT;
-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`login_name` varchar(30) NOT NULL COMMENT '登录账号',
`password` varchar(64) DEFAULT NULL COMMENT '登陆密码',
`status` char(1) DEFAULT '0' COMMENT '帐号状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户信息表';
-- ----------------------------
-- Records of sys_user
-- ----------------------------
BEGIN;
INSERT INTO `sys_user` (`user_id`, `login_name`, `password`, `status`, `del_flag`) VALUES (1, 'admin', '$2a$10$SnMMruuWQmEEKNMqREDb0e4jfaqJeZviOFjxQRwq.9A7PM6Z0xo5W', '0', '0');
INSERT INTO `sys_user` (`user_id`, `login_name`, `password`, `status`, `del_flag`) VALUES (2, 'user', '$2a$10$96vbFKuEmMlObg1bPqevdOJybTp2cAesJZ5uJBqR797qxnVWx12Wi', '0', '0');
INSERT INTO `sys_user` (`user_id`, `login_name`, `password`, `status`, `del_flag`) VALUES (3, 'developers', '$2a$10$BOpzjv4hmZQB1ydsaDTvZ.Cvyq4.kDty2/ghrcVKhetsTD1sKJaIu', '0', '0');
COMMIT;
-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`role_id` bigint NOT NULL COMMENT '角色ID',
PRIMARY KEY (`user_id`,`role_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户和角色关联表';
-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
BEGIN;
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (1, 1);
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (1, 3);
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (2, 2);
INSERT INTO `sys_user_role` (`user_id`, `role_id`) VALUES (3, 3);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
3. 完成初始配置
接下来在之前的 Maven
项目中还是创建第四个子模块 db-spring-security
由于需要操作数据库,在 Maven
主目录pom文件中,追加 mysql数据库驱动
以及 mybatis-plus
,博主使用的是 HikariCP
连接池 ,直接引入 spring-boot-starter-data-jpa
即可,Spring官方默认支持
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<!--使用 HikariCP 连接池-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>3.5.9</version>
</dependency>
最后配置yml文件,运行项目确保项目能正常链接数据库且启动成功
server:
port: 8083
spring:
application:
name: db-spring-security #最新Spring Security实战教程(五)基于数据库的动态用户认证
datasource:
url: jdbc:mysql://localhost:3306/slave_db?useSSL=false&serverTimezone=UTC
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
maximum-pool-size: 5
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 开启驼峰转换
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL
cache-enabled: true # 开启二级缓存
global-config:
db-config:
logic-delete-field: delFlag # 逻辑删除字段
logic-delete-value: 1 # 删除值
logic-not-delete-value: 0 # 未删除值
4. MyBatis-Plus实体定义
在确保数据库能正常链接后,接下来就跟着博主一起编写我们的业务代码吧
❶ 用户实体(实现UserDetails)
@Data
@TableName("sys_user")
public class SysUser implements UserDetails {
@TableId(type = IdType.AUTO)
private Long userId;
@TableField("login_name")
private String username; // Spring Security认证使用的字段
private String password;
private String status;
private String delFlag;
@TableField(exist = false)
private List<SysRole> roles;
// 实现UserDetails接口
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream()
.flatMap(role -> role.getMenus().stream())
.map(menu -> new SimpleGrantedAuthority(menu.getPerms()))
.collect(Collectors.toList());
}
@Override
public boolean isAccountNonExpired() { return true; }
@Override
public boolean isAccountNonLocked() {
return "0".equals(status);
}
@Override
public boolean isCredentialsNonExpired() { return true; }
@Override
public boolean isEnabled() {
return "0".equals(delFlag);
}
}
❷ 角色实体
@Data
@TableName("sys_role")
public class SysRole {
@TableId(type = IdType.AUTO)
private Long roleId;
private String roleName;
private String roleKey;
}
5. MyBatis-Plus Mapper配置
完成实体类的创建,我们开始配置Mapper实现数据库的业务查询处理,为了快速演示,博主整理就不构建mapper.xml了,直接在Mapper上写查询SQL
UserMapper接口 : 主要是通过用户角色中间表获取角色信息
@Mapper
public interface UserMapper extends BaseMapper<SysUser> {
@Select("SELECT r.* FROM sys_role r " +
"JOIN sys_user_role ur ON r.role_id = ur.role_id " +
"WHERE ur.user_id = #{userId}")
List<SysRole> selectRolesByUserId(Long userId);
}
6. 自定义UserDetailsService实现
自定义 UserDetailsService
继承 UserDetailsService
,重写 loadUserByUsername
方法,注入 UserMapper
通过用户名查询数据库数据,同时将用户的角色集合一并赋值;
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 查询基础用户信息
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getUsername, username);
SysUser user = userMapper.selectOne(wrapper);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 2. 加载角色
List<SysRole> roles = userMapper.selectRolesByUserId(user.getUserId());
user.setRoles(roles);
// 3. 检查账户状态
if (!user.isEnabled()) {
throw new DisabledException("用户已被禁用");
}
return user;
}
}
7. Spring Security配置文件与测试
@Configuration
@RequiredArgsConstructor
public class DbSecurityConfig {
private final UserDetailsServiceImpl userDetailsService;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.
authorizeHttpRequests(authorize -> authorize
.requestMatchers("/admin/**").hasAnyAuthority("ADMIN") //测试验证ADMIN角色方可访问
.anyRequest().authenticated())
.userDetailsService(userDetailsService)
.formLogin(withDefaults())
.logout(withDefaults())
;
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
最后编写测试Controller,当用户登陆认证成功,则默认返回当前用户数据信息
@RestController
@RequiredArgsConstructor
public class DemoDbController {
@GetMapping("/admin/view")
public ResponseEntity<String> admin() {
return ResponseEntity.ok("管理员ROLE_ADMIN角色访问ok");
}
@GetMapping("/")
public ResponseEntity<SysUser> index(Authentication authentication) {
SysUser principal = (SysUser)authentication.getPrincipal();
return ResponseEntity.ok(principal);
}
}
最终完整代码结构如下:小伙伴们可以根据自己需求构建项目测试
启动项目,在 Spring Security
提供的默认登陆页中输入账号密码,进行登陆!默认返回当前登陆用户信息
并切换不同的用户进行登陆测试访问 /admin/view ,以确定只有ADMIN
角色的用户可以访问!
总结
本章节演示了使用 MyBatis-Plus
进行数据库操作,动态加载用户、角色权限,并将其转换为 Spring Security
的 GrantedAuthority
列表,从而实现基于数据库的动态用户认证。并提供了一个简单的 Controller 用于测试和查看当前登录用户的信息。
至此关于Spring Security身份认证的 基于内存
以及 基于数据库
的两种方式均讲解完了,下一个章节我们要进入授权Authorization的相关讲解,小伙伴们耐心等待…
如果本本章内容对您有所帮助,希望 一键三连 给博主一点点鼓励,如果您有任何疑问或建议,请随时留言讨论!
下一章:最新Spring Security实战教程(六)基于数据库的ABAC属性权限模型实战开发
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/lhmyy521125/article/details/146305102