基于角色的访问控制(RBAC)实现资源权限管理
·
基于RBAC模型的后台权限管理系统

项目概述
这是一个基于 Spring Boot + Spring Security + MyBatis-Plus 的企业级后台权限管理系统,实现了完整的用户认证、授权、会话管理和动态权限控制功能。
技术架构
核心技术栈
- 后端框架: Spring Boot 2.7.6
- 安全框架: Spring Security 5.7.5
- 持久层: MyBatis-Plus 3.5.3.1
- 数据库: MySQL 8.0
- 缓存/会话: Redis
- 模板引擎: Thymeleaf 3.0.15
- 开发语言: Java 17
主要功能模块
1. 身份认证模块
- ✅ 表单登录认证
- ✅ 验证码校验(图片验证码)
- ✅ Remember-Me 记住我功能(Token 持久化到数据库)
- ✅ 自定义登录成功/失败处理器
2. 权限管理模块
- ✅ 动态权限控制: 基于
FilterInvocationSecurityMetadataSource和AccessDecisionManager实现运行时权限加载 - ✅ RBAC 模型: 用户-角色-权限三层权限体系
- ✅ URL 级别权限控制
- ✅ 自定义权限不足处理 handler
3. 会话管理
- ✅ Session 存储到 Redis(支持分布式部署)
- ✅ Session 超时时间: 600 秒(10 分钟)
- ✅ 退出登录自动清理 Token
4. 业务管理模块
- 👤 用户管理: 用户增删改查、角色分配
- 🎭 角色管理: 角色增删改查、权限关联
- 🔐 权限管理: 权限增删改查、动态刷新
- 📦 商品管理: 商品 CRUD、显示/隐藏控制
5. 跨域支持
- ✅ 完整的 CORS 配置
- ✅ 支持携带凭证(Cookie、Authorization)
- ✅ 预检请求缓存 1 小时
系统配置
服务配置
- 端口: 8080
- Session 超时: 600 秒
数据库配置
- 类型: MySQL 8.0
- 地址: 127.0.0.1:3306
- 数据库名: security_db
Redis 配置
- 地址: 127.0.0.1:6379
- 数据库: DB 1
- 用途: Session 共享、权限缓存
模板引擎
- 引擎: Thymeleaf
- 模式: LEGACYHTML5
- 缓存: 关闭(开发环境)
- 编码: UTF-8
核心特性
🔒 安全性
- 密码加密: BCrypt 强哈希算法
- CSRF 防护: 已禁用(根据业务需求)
- XSS 防护: Thymeleaf 自动转义
- 点击劫持防护: 同源 iframe 限制
⚡ 性能优化
- 静态资源忽略: CSS/JS/Images 不经过 Security 过滤器
- Redis 会话: 减轻服务器内存压力
- 动态权限缓存: 减少数据库查询次数
🎯 可扩展性
- 动态权限: 新增权限无需重启应用
- 模块化设计: Handler、Filter、Service 分层清晰
- RESTful API: 支持前后端分离扩展
适用场景
- 🏢 企业内部管理系统
- 🎓 学习 Spring Security 完整实践
- 🔧 RBAC 权限模型参考实现
- 🌐 分布式会话管理示例
项目结构
security-management/
├── config/ # 安全配置类
├── controller/ # 控制器层
├── pojo/ # 实体类
├── handler/ # 认证/授权处理器
├── filter/ # 自定义过滤器
├── service/ # 业务逻辑层
├── mapper/ # 数据访问层
└── resources/
└── templates/ # Thymeleaf 模板
核心实现方案
RBAC模型数据库设计
- 用户表
sys_user包含username、password(BCrypt加密)、status等字段 - 角色表
sys_role定义角色编码和名称 - 权限表
sys_permission存储权限标识和描述 - 关联表
sys_user_role和sys_role_permission建立多对多关系
#数据库设计实现
#用户表(sys_user)
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL COMMENT 'BCrypt加密存储',
status TINYINT DEFAULT 1 COMMENT '0-禁用 1-启用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
#角色表(sys_role)
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
role_code VARCHAR(50) NOT NULL UNIQUE COMMENT '角色编码',
role_name VARCHAR(50) NOT NULL COMMENT '角色名称',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
#权限表(sys_permission)
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
perm_code VARCHAR(100) NOT NULL UNIQUE COMMENT '权限标识',
perm_desc VARCHAR(200) COMMENT '权限描述',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP
);
#关联关系实现
#用户-角色关联表(sys_user_role)
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES sys_user(id),
FOREIGN KEY (role_id) REFERENCES sys_role(id)
);
#角色-权限关联表(sys_role_permission)
CREATE TABLE sys_role_permission (
role_id BIGINT NOT NULL,
perm_id BIGINT NOT NULL,
PRIMARY KEY (role_id, perm_id),
FOREIGN KEY (role_id) REFERENCES sys_role(id),
FOREIGN KEY (perm_id) REFERENCES sys_permission(id)
);
典型查询示例
查询用户权限列表
SELECT DISTINCT p.perm_code
FROM sys_user u
JOIN sys_user_role ur ON u.id = ur.user_id
JOIN sys_role_permission rp ON ur.role_id = rp.role_id
JOIN sys_permission p ON rp.perm_id = p.id
WHERE u.username = ? AND u.status = 1;
索引优化
在username、role_code和perm_code字段上建立唯一索引
在关联表的外键字段上建立普通索引以提高连接查询性能
CREATE INDEX idx_user_role ON sys_user_role(user_id, role_id);
CREATE INDEX idx_role_perm ON sys_role_permission(role_id, perm_id);
BCrypt密码加密示例
String encodedPassword = BCrypt.hashpw(rawPassword, BCrypt.gensalt());
// 验证密码
boolean matches = BCrypt.checkpw(rawPassword, encodedPassword);
动态权限控制实现
自定义元数据源
/**
* @author weh
* @version 1.0
* @description: 自定义元数据源
* @date 2026/5/11 22:35
*/
@Component
public class DynamicSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
private PermissionService permissionService;
@Override
public Collection<ConfigAttribute> getAttributes(Object object) {
String requestUrl = ((FilterInvocation) object).getRequestUrl();
List<Permission> permissionList = permissionService.listAll();
for (Permission permission : permissionList) {
if (new AntPathRequestMatcher(permission.getUrl()).matches(
((FilterInvocation) object).getRequest())) {
return SecurityConfig.createList(permission.getPermissionCode());
}
}
return null;
}
}
安全配置关键代码
自定义的UserDetailsService接口实现类
用于获取用户信息
package com.weh.service.impl;
import com.weh.pojo.Permission;
import com.weh.pojo.User;
import com.weh.service.PermissionService;
import com.weh.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
/**
* @author weh
* @version 1.0
* @description: 这里是自定义的MyUserDetailsService,用于获取用户信息
* 通过RBAC模型进行权限控制
* @date 2026/5/7 22:49
*/
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private PermissionService permissionService;
@Autowired
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("====="+username);
User user = userService.findByUsername(username);
if (Objects.isNull( user)){
throw new UsernameNotFoundException("用户不存在");
}
Collection<GrantedAuthority> authorities = new ArrayList<>();
List<Permission> permissionList = permissionService.findByUserId(user.getId());
// 通过用户id查询权限列表,然后添加权限标签
for (Permission permission : permissionList){
authorities.add(new SimpleGrantedAuthority(permission.getPermissionTag()));
}
// 创建UserDetails对象,这里只是作为实例演示,可自行重写
UserDetails userDetails = new org.springframework.security.core.userdetails.User
(username,user.getPassword(),//noop不使用密码加密 , bcrypt使用加密算法
true,// 用户是否启用
true,// 用户是否过期
true,// 用户凭证是否过期
true,// 用户是否锁定
authorities);
return userDetails;
}
}
Spring Security配置类
package com.weh.config;
import com.weh.filter.ValidateFilter;
import com.weh.handler.*;
import com.weh.service.impl.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import javax.sql.DataSource;
/**
* Spring Security 安全配置类
* <p>
* 负责配置应用的身份认证、授权、跨域、会话管理等安全策略。
* 采用动态权限管理,支持运行时从数据库加载权限配置。
* </p>
*
* @author weh
* @version 1.0
* @date 2026/5/7 21:37
*/
@Configuration
//@EnableGlobalMethodSecurity(prePostEnabled = true) // 启用方法权限控制,比如@PreAuthorize , @PostAuthorize ,@PreFilter , @PostFilter
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private MyLogoutSuccessHandler myLogoutSuccessHandler;
@Autowired
private ValidateFilter validateFilter;
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Autowired
private MyAccessDecisionManager myAccessDecisionManager;
@Autowired
private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;
/**
* 配置密码编码器 Bean
* <p>
* 使用 BCrypt 强哈希算法对密码进行加密存储和验证。
* </p>
*
* @return PasswordEncoder 实例
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置身份认证管理器
* <p>
* 指定自定义的用户详情服务(MyUserDetailsService)和密码编码器,
* 用于在登录时从数据库加载用户信息并验证密码。
* </p>
*
* @param auth AuthenticationManagerBuilder 构建器
* @throws Exception 配置异常
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService)// 使用自定义用户认证
.passwordEncoder(passwordEncoder()); // 密码编码器
}
/**
* 配置忽略静态资源的安全过滤
* <p>
* 将 CSS、图片、JS、验证码等静态资源路径排除在 Security 过滤器链之外,
* 提高性能并避免不必要的认证检查。
* </p>
*
* @param web WebSecurity 构建器
*/
@Override
public void configure(WebSecurity web) {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/css/**", "/images/**", "/js/**","/code/**");
}
/**
* 配置 HTTP 安全策略
* <p>
* 包括验证码过滤器、动态权限管理、表单登录、登出、记住我、跨域等功能。
* </p>
*
* @param http HttpSecurity 构建器
* @throws Exception 配置异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//===================================================================================
// 在用户名密码认证过滤器之前添加验证码校验过滤器
http.addFilterBefore(validateFilter, UsernamePasswordAuthenticationFilter.class);
// ==================================================================================================
// 权限控制(必须要放到anyRequest().authenticated()之前的位置,不然报错)
// http.authorizeRequests().antMatchers("/user/**").hasRole("ADMIN"); // /user/** 路径下的所有请求,需要ADMIN角色才能访问
// // url权限控制, 设置多个角色用, 多个ip用and连接; 这里配置了ADMIN / PRODUCT角色并且IP为127.0.0.1的ip才能访问
// http.authorizeRequests().antMatchers("/product/**").access("hasAnyRole('ADMIN','PRODUCT')and hasIpAddress('127.0.0.1')");
// http.authorizeRequests().antMatchers("/user/**").access("@myAuthorizationConfig.check(authentication,request)");
//hasRole和hasAuthority的区别就是权限标签,
// hasRole是角色权限,hasAuthority是权限标签
//hasRole("ADMIN") --用户授予 new SimpleGrantedAuthority("ROLE_ADMIN")
//hasAuthority("user:getAll") --用户授予 new SimpleGrantedAuthority("user:getAll")
// 查询所有权限列表
// List<Permission> permissions = permissionService.list();
// // 设置每个接口url进行权限控制,通过权限标签permissionTag进行权限控制
// for (Permission permission : permissions){
// http.authorizeRequests().antMatchers(permission.getPermissionUrl())
// .hasAuthority(permission.getPermissionTag());
// }
// 配置动态权限管理:通过自定义的元数据源和决策管理器实现运行时权限控制
http.authorizeRequests()
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
fsi.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);
fsi.setAccessDecisionManager(myAccessDecisionManager);
return fsi;
}
})
.and().exceptionHandling().accessDeniedHandler(myAccessDeniedHandler); // 添加权限访问处理类
// ===================================================================================
//用HttpBasic认证(不安全)
// http.httpBasic()
// .and().authorizeRequests() // 配置权限,默认所有请求都需要认证
// .anyRequest() // 所有请求
// .authenticated(); // 需要认证
// 用配置表单认证(较为安全)
http.formLogin()
// 自定义登录页面(用static文件作为静态资源是可以直接写login.html),但是项目中用的是templates模板,路径需改为映射的路径
.loginPage("/toLoginPage")
.loginProcessingUrl("/login") // 登录处理接口url
.usernameParameter("username") // 用户名参数名
.passwordParameter("password") // 密码参数名
.defaultSuccessUrl("/") // 登录成功后默认跳转的页面 / 自动登录后跳转到主页
.successHandler(myAuthenticationSuccessHandler) // 引入自定义登录成功处理器
.failureHandler(myAuthenticationFailureHandler) // 引入自定义登录失败处理器
.and()
.logout().logoutUrl("/logout")// 登出接口url,如果有remember-me功能,系统会自动将token进行删除
.logoutSuccessHandler(myLogoutSuccessHandler) // 引入自定义登出成功处理器
.and()
.rememberMe()// 开启记住我功能
.rememberMeParameter("remember-me")// 记住我参数名
.tokenValiditySeconds(60 * 60 * 24 * 7)// token有效时间,默认14天
.tokenRepository(getPersistentTokenRepository()) // 持久化token,将token保存到数据库中persistent_logins表
.and()
.authorizeRequests().antMatchers("/toLoginPage").permitAll()//放行登录页面和验证码接口
.anyRequest()
.authenticated();
//加载同源域名下iframe页面
http.headers().frameOptions().sameOrigin();
// ==================================================================================================
// session会话设置
// http.sessionManagement()
// .invalidSessionUrl("/toLoginPage") // session失效后跳转的页面
// .maximumSessions(1)// 设置session最大会话数量,1同一时间只能有一个用户登录,互踢
// .maxSessionsPreventsLogin(true) //当达到最大数量后,不允许再登录
// .expiredUrl("/toLoginPage"); // session过期后跳转的页面
// 禁用csrf,若开启csrf,则需要配置csrfTokenRepository, 否则会报403错误
http.csrf().disable();
// 开启跨域支持,允许跨域
http.cors().configurationSource(corsConfigurationSource());
}
/**
* 配置持久化 Token 仓库(记住我功能)
* <p>
* 将 Remember-Me 的 Token 存储到数据库中,支持跨会话保持登录状态。
* 首次启动时可设置 setCreateTableOnStartup(true) 自动创建 persistent_logins 表。
* </p>
*
* @return PersistentTokenRepository 实例
*/
@Bean
public PersistentTokenRepository getPersistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);// 设置数据源
//启动时帮助我们自动创建一张表persistent_logins, 第一次启动设置true 第二次启动设置false或者注释
// jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
@Autowired
private CorsConfiguration corsConfiguration;
/**
* 配置跨域资源共享(CORS)信息源
* <p>
* 允许所有来源、方法、请求头的跨域请求,并支持携带凭证(Cookie、Authorization 等)。
* 预检请求缓存时间为 1 小时,减少浏览器 OPTIONS 请求次数。
* </p>
*
* @return CorsConfigurationSource 跨域配置源
*/
private CorsConfigurationSource corsConfigurationSource() {
// 允许跨域的站点(使用 allowedOriginPattern 支持通配符,兼容 allowCredentials)
corsConfiguration.addAllowedOriginPattern("*");
//用spring boot 2.4.0以上版本不支持
// corsConfiguration.addAllowedOrigin("*");
// 允许跨域的 HTTP 方法(GET, POST, PUT, DELETE, OPTIONS 等)
corsConfiguration.addAllowedMethod("*");
// 允许跨域的请求头(Content-Type, Authorization 等)
corsConfiguration.addAllowedHeader("*");
// 允许携带凭证(cookie、session、Authorization 头等)
corsConfiguration.setAllowCredentials(true);
// 预检请求(OPTIONS)的缓存时间(秒),减少预检请求次数
corsConfiguration.setMaxAge(3600L);
// 创建基于 URL 的跨域配置源
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
// 对所有 URL 路径应用此跨域配置
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
return urlBasedCorsConfigurationSource;
}
}
会话管理配置
RedisSession配置
spring:
session:
store-type: redis
timeout: 600s
redis:
namespace: spring:session
redis:
host: 127.0.0.1
port: 6379
database: 1
前端权限控制方案
Thymeleaf安全表达式
<div sec:authorize="hasAuthority('user:add')">
<button class="btn-add">新增用户</button>
</div>
性能优化措施
权限缓存实现
@Cacheable(value = "permissionCache", key = "'all_permissions'")
public List<Permission> listAll() {
return baseMapper.selectList(null);
}
@CacheEvict(value = "permissionCache", key = "'all_permissions'")
public void clearPermissionCache() {
// 清空缓存
}
异常处理机制
自定义AccessDeniedHandler
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException e) {
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(
Result.error(403, "权限不足")));
}
}
后台管理系统登录
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)