Spring Boot 集成 Spring Security()数据访问权限
Spring Boot 集成 Spring Security()数据访问权限
Spring Boot 集成 Spring Security()数据访问权限
在前面的文章中我们介绍了《Spring Boot 集成 Spring Security(1)认证》和《Spring Boot 集成 Spring Security(2)授权》,这篇博客将介绍如何在 Spring Boot 项目中,整合 Spring Security 和 MyBatis-Plus ,轻松实现基于数据库的用户访问控制、权限管理。
推荐框架
基于SpringBoot+Vue前后端分离的Java快速开发框架
项目简介:基于 JDK 17、Spring Boot 、Spring Security 6、JWT、Redis、Mybatis-Plus、Knife4j等构建后端,基于Vue 、Element-Plus 、TypeScript等构建前端的分离单体权限管理系统。
项目地址:
后端:
gitee: .git
gitcode: .git
前端:
gitee: .git
.git
觉着有帮助,给个Star再走呗 搜“Harry技术”,关注我,带你看不一样的人间烟火!
新建项目
springboot-security-mysql-example 引入依赖
引入MyBatis-Plus依赖
MyBatis-Plus 是一个 MyBatis 的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
代码语言:xml复制注意事项版本 .5.9+ 开始修改为可选依赖,具体查看下文 maven bom 部分。
<mybatisplus.version>.5.9</mybatisplus.version>
代码语言:xml复制 <!-- MyBatis-Plus ;
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
</dependency>
代码语言:xml复制 <dependencyManagement>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-bom</artifactId>
<version>${mybatisplus.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
创建表结构
这里我们定义三张表,来实现用户角权限的操作
-- 用户表
CREATE TABLE `sys_user` (
`id` BIGIT OT ULL AUTO_ICREMET COMMET 'ID',
`username` VARCHAR ( 64 ) DEFAULT ULL COMMET '用户名',
`password` VARCHAR ( 64 ) DEFAULT ULL COMMET '密码',
`` CHAR ( 1 ) DEFAULT '0' COMMET '性别 0 男 1 女 2 未知',
`nick_name` VARCHAR ( 64 ) DEFAULT ULL COMMET '昵称',
`status` CHAR ( 1 ) DEFAULT '1' COMMET '账号状态 0 禁用 1 启用',
`valid` IT DEFAULT '1' COMMET '有效状态 0 无效 1 有效',
PRIMARY KEY ( `id` )
) EGIE = IODB COMMET = '用户';
ISERT ITO `sys_user` (`id`, `username`, `password`, ``, `nick_name`, `status`, `valid`) VALUES (1, 'admin', '$2a$10$xZdonloiiL6YfoLZv6mrJuvxtD28uHPIKkVDpQKBuZxzMDpTf8uK', '0', '管理员张三', '1', 1);
ISERT ITO `sys_user` (`id`, `username`, `password`, ``, `nick_name`, `status`, `valid`) VALUES (2, 'user', '$2a$10$evM9Sfvu/E.ykWWOf6beTltPvuc6XjwW/qIhagSjlsTfi9l26Ba', '0', '用户李四', '1', 1);
-- 角表
CREATE TABLE `sys_role` (
`id` BIGIT OT ULL AUTO_ICREMET COMMET 'ID',
`name` VARCHAR ( 64 ) DEFAULT ULL COMMET '角名',
`code` VARCHAR ( 64 ) DEFAULT ULL COMMET '密码',
`status` CHAR ( 1 ) DEFAULT '1' COMMET '状态 0 禁用 1 启用',
`valid` IT DEFAULT '1' COMMET '有效状态 0 无效 1 有效',
PRIMARY KEY ( `id` )
) EGIE = IODB COMMET = '角';
ISERT ITO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (1, '管理员', 'ROOT', '1', 1);
ISERT ITO `sys_role` (`id`, `name`, `code`, `status`, `valid`) VALUES (2, '普通用户', 'USER', '1', 1);
-- 用户角关系表
CREATE TABLE `sys_user_role` (
`id` BIGIT OT ULL AUTO_ICREMET COMMET 'ID',
`user_id` bigint DEFAULT ULL COMMET '用户ID',
`role_id` bigint DEFAULT ULL COMMET '角ID',
PRIMARY KEY (`id`) USIG BTREE
) EGIE = IODB COMMET = '用户角关系表';
ISERT ITO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (1, 1, 1);
ISERT ITO `sys_user_role` (`id`, `user_id`, `role_id`) VALUES (2, 2, 2);
代码语言:java复制默认设置账户密码
12456
,在数据库中使用加密后的密码,关于密码加密,可以使用下面的测试方法。
public static void main(String[] args) {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String result = ("12456");
// 输出加密后的密码
println(result);
// 对比加密后的密码和明文密码
println(("12456", result));
}
生成基础代码
这里我用了代码生成器插件,以提高生产效率,想具体了解,可以去上搭建使用。这里就不多说啦
本地代码勾选,使用 mybatis-plus
准备工作到这里基本上就可以了,接下来开始实现从数据库中读取用户角权限
application.yml
配置
代码语言:yaml复制spring:
thymeleaf:
# 设置Thymeleaf模板文件的前缀位置(默认是`src/main/resources/templates`)
prefix: classpath:/templates/
# 设置模板文件的后缀(默认是`.html`)
suffix: .html
# 设置模板模式(默认是HTML5,Thymeleaf 中为`HTML`)
mode: HTML
# 开启模板缓存(开发时建议关闭,生产时开启)
cache: false
datasource:
driver-class-name: jdbc.Driver
url: jdbc:mysql://127.0.0.1:06/security_data?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
#mybatis
mybatis-plus:
mapper-locati: classpath*:/mapper/**/*.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: cn.harry.*.domain
global-config:
#数据库相关配置
db-config:
#主键类型 AUTO:"数据库ID自增", IPUT:"用户输入ID", ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
id-type: AUTO
# 逻辑删除全局属性名(驼峰和下划线都支持)
logic-delete-field: valid
logic-delete-value: 0 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 1 # 逻辑未删除值(默认为 0)
banner: false
#原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
jdbc-type-for-null: 'null'
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
SecurityConfig 配置
要使用 Spring Security 进行用户认证,我们需要配置 SecurityConfig,并实现自定义的 UserDetailsService 来与数据库中的用户信息进行集成。
代码语言:java复制/**
* @author harry
* @ Harry技术
*/
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true) // 开启方法级别的权限控制
@RequiredArgsCtructor
public class SecurityConfig {
// 通过构造函数注入自定义UserDetailsService
private final UserDetailsService userDetailsService;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
// 公开访问
.requestMatchers("/").permitAll()
// 其他接口需认证
.anyRequest().authenticated()
)
.userDetailsService(userDetailsService)
// .exceptionHandling(exception -> exception
// .authenticationEntryPoint(restAuthenticationEntryPoint)
// .accessDeniedHandler(restfulAccessDeniedHandler)
// )
// 开启基于表单的登录
.formLogin(Customizer.withDefaults())
// // 开启注销功能
// .logout(Customizer.withDefaults())
// // 开启 HTTP Basic 认证
// .httpBasic(Customizer.withDefaults())
// // 开启 CSRF 防护
// .csrf(Customizer.withDefaults())
// // 开启跨域资源共享
// .cors(Customizer.withDefaults())
;
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
// 使用 BCrypt 进行密码加密
return new BCryptPasswordEncoder();
}
自定义 UserDetailsService
想从数据库加载用户信息,就需要创建一个自定义的 UserDetailsService 实现类,它的主要作用:
用户认证:
UserDetailsService 负责从数据源(如数据库、LDAP等)中加载用户特定的安全信息,包括用户名、密码和权限(角)。
Spring Security 使用 UserDetailsService 来验证用户提供的凭据是否正确。
用户授权:
加载用户的权限信息,以便在认证成功后进行授权检查。
权限信息通常包括用户的角(如 ROLE_ADMI, ROLE_USER 等),这些角用于控制用户可以访问的资源和操作。
代码语言:java复制/**
* 系统用户认证 service
*
* @author harry
* @ Harry技术
*/
@Slf4j
@Service
@RequiredArgsCtructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final SysUserMapper sysUserMapper;
private final SysUserRoleMapper sysUserRoleMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameotFoundException {
try {
// 获取登录用户信息
SysUser user = sysUserMapper.selectByUsername(username);
// 用户不存在、用户停用 等校验 TODO
Long useId = user.getId();
// 获取角
Set<String> roles = sysUserRoleMapper.listRoleKeyByUserId(useId);
return new SysUserDetails(user, roles, username);
} catch (Exception e) {
("loadUserByUsername error", e);
}
return null;
}
}
我们根据数据库中的用户信息加载用户,并将角转换为 Spring Security 能识别的格式。我们写一个SysUserDetails
类来实现自定义Spring Security 用户对象。
/**
* 自定义 Spring Security 用户对象
*
* @author harry
* @ Harry技术
*/
@Data
@oArgsCtructor
public class SysUserDetails implements UserDetails {
private String username;
private SysUser sysUser;
private Collection<SimpleGrantedAuthority> authorities;
public SysUserDetails(SysUser user, Set<String> roles, String username) {
this.sysUser = user;
Set<SimpleGrantedAuthority> authorities;
if (CollectionUtil.isotEmpty(roles)) {
// 标识角 前面加上 ROLE_
authorities = roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(());
} else {
authorities = ();
}
this.authorities = authorities;
this.username = username;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 返回当前用户的权限
return authorities;
}
@Override
public String getPassword() {
return sysUser.getPassword();
}
@Override
public String getUsername() {
return this.username;
}
/**
* 是否可用 ,禁用的用户不能身份验证
*
* @return 是否可用
*/
@Override
public boolean isEnabled() {
return StatusEnums.EABLE.getKey().equals(sysUser.getStatus());
}
}
创建测试
- 页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.content {
width: 800px;
height: 800px;
text-align: center;
line-height: 100px;
font-size: 20px;
flex: 1;
flex-direction: column;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="content">
<!--去登陆 -->
<a href="/login">去登陆</a>
<!-- admin/info 接口 -->
<a href="/admin/info">访问 admin/info 接口</a>
<!-- 去首页 -->
<a href="/user/info">访问 user/info 接口</a>
<!--退出-->
<a href="/logout">退出</a>
</div>
</body>
</html>
- 接口
改写接口admin/info
,并配置@PreAuthorize("hasRole('ROOT')")
只有 ADMI 角才能访问
/**
* @author harry
* @ Harry技术
* Spring Boot 集成 Spring Security(2) 授权:
*/
@Slf4j
@RestController
public class AdminController {
@GetMapping("/admin/info")
@PreAuthorize("hasRole('ROOT')") // 只有 ADMI 角才能访问
public SysUserDetails adminInfo() {
// 获取当前登录的用户信息
SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("当前登录的用户信息:{}", ());
return user;
}
}
改写接口user/info
,并配置@PreAuthorize("hasRole('USER')")
只有 USER 角才能访问
@Slf4j
@RestController
public class UserController {
@GetMapping("/user/info")
@PreAuthorize("hasRole('USER')") // 只有 user 角才能访问
public SysUserDetails userInfo() {
// 获取当前登录的用户信息
SysUserDetails user = (SysUserDetails) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.info("当前登录的用户信息:{}", ());
return user;
}
}
- 1.登录admin账户
访问 admin/info 接口
访问 user/info 接口
- 2.登录user用户
访问 admin/info 接口
访问 user/info 接口
通过上面的测试用例,通过定义用户和角实体、实现自定义的 UserDetailsService,实现了数据库驱动的用户认证和基于角的授权机制。这种结合方式不仅在安全性上提供了极大的灵活性,也让数据管理变得更加简洁高效。
关注我,在后续的文章中,我们进一步探讨如果使用JWT、OAuth2 等机制、使用Redis作为缓存来强化认证与授权的实现。
示例源码:关注“Harry技术”,回复 security 获取源码地址。
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
推荐阅读
留言与评论(共有 8 条评论) |
本站网友 mindbridge | 22分钟前 发表 |
map-underscore-to-camel-case | |
本站网友 广西男科医院 | 11分钟前 发表 |
'$2a$10$evM9Sfvu/E.ykWWOf6beTltPvuc6XjwW/qIhagSjlsTfi9l26Ba' | |
本站网友 叙利亚局势吧 | 29分钟前 发表 |
map-underscore-to-camel-case | |
本站网友 广州月嫂 | 23分钟前 发表 |
true cache-enabled | |
本站网友 期房和现房 | 10分钟前 发表 |
可以去上搭建使用 | |
本站网友 中国市场监测中心 | 27分钟前 发表 |
/mapper/**/*.xml #实体扫描 | |
本站网友 蛋黄果 | 4分钟前 发表 |
`status` |