前言
RBAC(Role-Based Access Control)是基于角色的访问控制模型,广泛应用于企业权限管理系统。本文将介绍 Spring Boot 实现 RBAC 权限控制的完整方案。
数据库设计
1. 表结构
-- 用户表
CREATE TABLE sys_user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 角色表
CREATE TABLE sys_role (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
code VARCHAR(50) NOT NULL UNIQUE,
description VARCHAR(200),
status TINYINT DEFAULT 1
);
-- 权限表
CREATE TABLE sys_permission (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
code VARCHAR(100) NOT NULL UNIQUE,
type TINYINT NOT NULL, -- 1:菜单 2:按钮 3:接口
parent_id BIGINT DEFAULT 0,
path VARCHAR(200),
method VARCHAR(10),
status TINYINT DEFAULT 1
);
-- 用户角色关联表
CREATE TABLE sys_user_role (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY (user_id, role_id)
);
-- 角色权限关联表
CREATE TABLE sys_role_permission (
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
PRIMARY KEY (role_id, permission_id)
);
2. 实体类
@Data
@Entity
@Table(name = "sys_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
private String email;
private String phone;
private Integer status;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "sys_user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private List<Role> roles = new ArrayList<>();
}
@Data
@Entity
@Table(name = "sys_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
private String description;
private Integer status;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "sys_role_permission",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "permission_id")
)
private List<Permission> permissions = new ArrayList<>();
}
@Data
@Entity
@Table(name = "sys_permission")
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
private Integer type;
private Long parentId;
private String path;
private String method;
private Integer status;
}
用户详情服务
1. 实现 UserDetailsService
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(
"用户不存在:" + username
));
if (user.getStatus() != 1) {
throw new DisabledException("用户已禁用");
}
List<GrantedAuthority> authorities = getAuthorities(user);
return org.springframework.security.core.userdetails.User
.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(authorities)
.disabled(user.getStatus() != 1)
.build();
}
private List<GrantedAuthority> getAuthorities(User user) {
List<GrantedAuthority> authorities = new ArrayList<>();
// 添加角色
for (Role role : user.getRoles()) {
authorities.add(new SimpleGrantedAuthority("ROLE_" + role.getCode()));
// 添加权限
for (Permission permission : role.getPermissions()) {
if (permission.getStatus() == 1) {
authorities.add(new SimpleGrantedAuthority(permission.getCode()));
}
}
}
return authorities;
}
}
2. 自定义 UserPrincipal
@Data
@AllArgsConstructor
public class UserPrincipal implements UserDetails {
private Long id;
private String username;
private String password;
private List<GrantedAuthority> authorities;
private boolean enabled;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
}
动态权限控制
1. 权限元数据
@Data
public class PermissionMeta {
private Long id;
private String code;
private String name;
private Integer type; // 1:菜单 2:按钮 3:接口
private String path;
private String method;
private Long parentId;
private List<PermissionMeta> children;
}
2. 权限服务
@Service
@RequiredArgsConstructor
public class PermissionService {
private final PermissionRepository permissionRepository;
/**
* 获取用户权限列表
*/
public List<String> getUserPermissions(Long userId) {
return permissionRepository.findUserPermissions(userId);
}
/**
* 获取用户角色列表
*/
public List<String> getUserRoles(Long userId) {
return permissionRepository.findUserRoles(userId);
}
/**
* 获取所有权限树
*/
public List<PermissionMeta> getPermissionTree() {
List<Permission> all = permissionRepository.findAll();
return buildTree(all, 0L);
}
/**
* 获取用户权限树
*/
public List<PermissionMeta> getUserPermissionTree(Long userId) {
List<String> codes = getUserPermissions(userId);
List<Permission> permissions =
permissionRepository.findByCodeIn(codes);
return buildTree(permissions, 0L);
}
private List<PermissionMeta> buildTree(List<Permission> permissions, Long parentId) {
return permissions.stream()
.filter(p -> Objects.equals(p.getParentId(), parentId))
.map(p -> {
PermissionMeta meta = new PermissionMeta();
meta.setId(p.getId());
meta.setCode(p.getCode());
meta.setName(p.getName());
meta.setType(p.getType());
meta.setPath(p.getPath());
meta.setMethod(p.getMethod());
meta.setParentId(p.getParentId());
meta.setChildren(buildTree(permissions, p.getId()));
return meta;
})
.collect(Collectors.toList());
}
}
3. 动态授权管理器
@Component
public class DynamicAuthorizationManager implements AuthorizationManager<RequestAttributeHolder> {
private final PermissionService permissionService;
public DynamicAuthorizationManager(PermissionService permissionService) {
this.permissionService = permissionService;
}
@Override
public AuthorizationDecision check(
Supplier<Authentication> authentication,
RequestAttributeHolder object
) {
Authentication auth = authentication.get();
if (auth == null || !auth.isAuthenticated()) {
return new AuthorizationDecision(false);
}
HttpServletRequest request = object.getRequest();
String uri = request.getRequestURI();
String method = request.getMethod();
// 获取用户权限
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
List<String> permissions = permissionService.getUserPermissions(principal.getId());
// 检查是否有权限
boolean hasPermission = permissions.stream()
.anyMatch(code -> matchPermission(code, uri, method));
return new AuthorizationDecision(hasPermission);
}
private boolean matchPermission(String permissionCode, String uri, String method) {
// 权限码格式:method:path
String[] parts = permissionCode.split(":");
if (parts.length != 2) {
return false;
}
String permMethod = parts[0];
String permPath = parts[1];
return permMethod.equalsIgnoreCase(method) &&
pathMatcher.match(permPath, uri);
}
}
4. 配置动态授权
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final DynamicAuthorizationManager dynamicAuthorizationManager;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/api/auth/**", "/api/public/**").permitAll()
.anyRequest().access(dynamicAuthorizationManager)
);
return http.build();
}
}
方法级别授权
1. 启用方法安全
@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}
2. 使用注解
@Service
public class UserService {
/**
* 需要角色
*/
@PreAuthorize("hasRole('ADMIN')")
public List<UserDTO> getAllUsers() {
return userRepository.findAll();
}
/**
* 需要权限
*/
@PreAuthorize("hasAuthority('user:create')")
public UserDTO createUser(UserCreateDTO dto) {
return userRepository.save(convert(dto));
}
/**
* 基于数据授权
*/
@PreAuthorize("#userId == authentication.principal.id or hasRole('ADMIN')")
public UserDTO getUserById(Long userId) {
return userRepository.findById(userId).orElse(null);
}
/**
* 自定义表达式
*/
@PreAuthorize("@permissionService.hasPermission(#userId, 'user:read')")
public UserDTO getUser(Long userId) {
return userRepository.findById(userId).orElse(null);
}
}
3. 自定义权限评估器
@Component("permissionService")
public class PermissionEvaluator {
private final PermissionService permissionService;
public PermissionEvaluator(PermissionService permissionService) {
this.permissionService = permissionService;
}
@PreAuthorize("hasRole('ADMIN')")
public boolean hasPermission(Long userId, String permissionCode) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null) {
return false;
}
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
// 管理员拥有所有权限
if (principal.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {
return true;
}
// 检查用户权限
List<String> permissions = permissionService.getUserPermissions(principal.getId());
return permissions.contains(permissionCode);
}
}
数据权限
1. 数据权限注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataPermission {
/**
* 部门 ID
*/
String deptId() default "";
/**
* 用户 ID
*/
String userId() default "";
}
2. 数据权限切面
@Aspect
@Component
@Order(1)
public class DataPermissionAspect {
@Around("@annotation(dataPermission)")
public Object around(ProceedingJoinPoint pjp, DataPermission dataPermission) throws Throwable {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
UserPrincipal principal = (UserPrincipal) auth.getPrincipal();
// 获取用户数据权限
DataScope dataScope = getDataScope(principal.getId());
// 添加过滤条件
DataScopeContext.set(dataScope);
try {
return pjp.proceed();
} finally {
DataScopeContext.clear();
}
}
private DataScope getDataScope(Long userId) {
// 从数据库获取用户的数据权限范围
return dataScopeRepository.findByUserId(userId);
}
}
3. 数据范围
@Data
public class DataScope {
/**
* 范围类型
* 1: 全部数据
* 2: 本部门及以下
* 3: 本部门
* 4: 仅本人
* 5: 自定义
*/
private Integer scopeType;
/**
* 自定义部门 ID 列表
*/
private List<Long> deptIds;
}
4. MyBatis 拦截器
@Intercepts({
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
@Component
public class DataPermissionInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
DataScope dataScope = DataScopeContext.get();
if (dataScope == null) {
return invocation.proceed();
}
MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
BoundSql boundSql = ms.getBoundSql(invocation.getArgs()[1]);
String sql = boundSql.getSql();
// 添加数据权限过滤
String filteredSql = addDataScopeFilter(sql, dataScope);
// 替换 SQL
MetaObject metaObject = SystemMetaObject.forObject(boundSql);
metaObject.setValue("sql", filteredSql);
return invocation.proceed();
}
private String addDataScopeFilter(String sql, DataScope dataScope) {
switch (dataScope.getScopeType()) {
case 1: // 全部数据
return sql;
case 2: // 本部门及以下
return sql + " AND dept_id IN (" +
buildDeptSql(dataScope.getDeptIds()) + ")";
case 3: // 本部门
return sql + " AND dept_id = " +
dataScope.getDeptIds().get(0);
case 4: // 仅本人
return sql + " AND create_by = " +
SecurityContextHolder.getContext().getName();
default:
return sql;
}
}
}
最佳实践
1. 权限初始化
@Component
@RequiredArgsConstructor
public class PermissionInitializer implements ApplicationRunner {
private final PermissionService permissionService;
@Override
public void run(ApplicationArguments args) {
// 初始化权限数据
permissionService.initPermissions();
}
}
2. 缓存权限
@Service
@RequiredArgsConstructor
public class PermissionService {
private final RedisTemplate<String, Object> redisTemplate;
@Cacheable(value = "permissions", key = "'user:' + #userId")
public List<String> getUserPermissions(Long userId) {
return permissionRepository.findUserPermissions(userId);
}
@CacheEvict(value = "permissions", key = "'user:' + #userId")
public void clearUserPermissionCache(Long userId) {
// 清除缓存
}
}
3. 权限变更通知
@Service
public class PermissionChangeService {
@Autowired
private ApplicationEventPublisher eventPublisher;
public void notifyPermissionChange(Long userId) {
eventPublisher.publishEvent(new PermissionChangeEvent(this, userId));
}
}
总结
RBAC 权限控制要点:
- ✅ 数据库设计 - 用户 - 角色 - 权限
- ✅ UserDetailsService - 加载用户和权限
- ✅ 动态授权 - 基于 URL 的动态权限
- ✅ 方法安全 - @PreAuthorize、自定义表达式
- ✅ 数据权限 - 数据范围过滤
- ✅ 最佳实践 - 缓存、初始化、通知
RBAC 是企业权限管理的基础模型。