控制权限系列之(2)手把手教你使用基于角色的权限控制

前一篇已经分析了多种权限模型,其中比较常用的是基于角色的权限控制。

基于角色的权限控制

表设计:

复制代码
用户表
用户--角色关系表
角色表
角色--菜单关系表
菜单表

权限标识格式:

复制代码
格式:xxx:xxx:xxx(模块:资源:操作) 三段式

权限架构的分层结构:

复制代码
应用层 (@RequirePermission注解)
    ↓
权限验证层 (PermissionValidator)
    ↓
权限上下文层 (PermissionContext - ThreadLocal)
    ↓
权限提供层 (PermissionProvider - 数据库/缓存)
    ↓
权限存储层 (PermissionStorage - Redis/Memory)

权限架构接口:

权限提供者接口 (PermissionProvider)

复制代码
 1 public interface PermissionProvider {
 2     // 获取用户的所有权限标识
 3     Set<String> getUserPermissions(Object userId);
 4     
 5     // 获取用户角色列表
 6     Set<String> getUserRoles(Object userId);
 7     
 8     // 检查用户是否为超级管理员
 9     boolean isSuperAdmin(Object userId);
10     
11     // 刷新用户权限缓存
12     void refreshUserPermissions(Object userId);
13 }
  • userId 使用 Object 类型,支持 String、Long 等不同ID类型
  • 支持超级管理员逻辑(拥有所有权限)
  • 提供刷新接口,支持权限变更后更新缓存

权限存储接口 (PermissionStorage)

复制代码
 1 public interface PermissionStorage {
 2     // 存储用户权限信息
 3     void storeUserInfo(String token, UserInfo userInfo);
 4     
 5     // 获取用户权限信息
 6     UserInfo getUserInfo(String token);
 7     
 8     // 删除用户权限信息
 9     void removeUserInfo(String token);
10     
11     // 刷新用户权限信息
12     void refreshUserInfo(String token, UserInfo userInfo);
13 }
  • 基于Token存储,支持分布式场景
  • 支持多种存储实现(Redis、Memory、数据库等)
  • 提供刷新接口,支持权限实时更新

权限匹配器接口 (PermissionMatcher)

复制代码
 1 public interface PermissionMatcher {
 2     // 匹配权限
 3     boolean match(String requiredPermission, Set<String> userPermissions);
 4     
 5     // 批量匹配(全部满足)
 6     boolean matchAll(Set<String> requiredPermissions, Set<String> userPermissions);
 7     
 8     // 批量匹配(任意一个满足)
 9     boolean matchAny(Set<String> requiredPermissions, Set<String> userPermissions);
10 }
  • 支持多种匹配策略(精确、通配符、正则等)
  • 支持批量匹配(AND/OR逻辑)
  • 可扩展自定义匹配规则

用户信息

复制代码
1 public class UserInfo {
2     private Object userId;                    // 用户ID
3     private String username;                  // 用户名
4     private Set<String> permissions;          // 权限集合
5     private Set<String> roles;                // 角色集合
6     private boolean superAdmin;               // 是否超级管理员
7     private Map<String, Object> attributes;   // 扩展属性
8 }
  • 使用 Object 类型支持不同ID类型
  • 提供扩展属性,支持业务字段存储
  • 轻量级设计,便于序列化和传输

权限上下文 (PermissionContext)

复制代码
 1 public class PermissionContext {
 2     private static final ThreadLocal<UserInfo> USER_INFO_HOLDER = new TransmittableThreadLocal<>();
 4     
 5     public static void set(UserInfo userInfo);
 6     public static UserInfo get();
 7     public static void clear();
 8     
 9     public static boolean hasPermission(String permission);
10     public static Set<String> getPermissions();
11 }
  • 使用 TransmittableThreadLocal 支持线程池传递
  • 提供便捷方法,简化权限判断
  • 请求结束后自动清理,避免内存泄漏

权限验证器接口 (PermissionValidator)

复制代码
 1 public interface PermissionValidator {
 2     // 验证单个权限
 3     boolean hasPermission(String permission);
 4     
 5     // 验证所有权限(AND)
 6     boolean hasAllPermissions(Set<String> permissions);
 7     
 8     // 验证任意权限(OR)
 9     boolean hasAnyPermission(Set<String> permissions);
10     
11     // 验证角色
12     boolean hasRole(String role);
13     
14     // 获取当前用户上下文
15     UserContext getCurrentUser();
16 }
  • 提供多种验证方式(单个、全部、任意)
  • 支持角色验证
  • 统一权限验证入口

实现策略

数据库权限获取

  1. 通过JdbcTemplate执行SQL查询
  2. SQL可配置化(支持不同数据库表结构)
  3. 支持超级管理员特殊处理
  4. 支持权限缓存
复制代码
 1 @Component
 2 public class DatabasePermissionProvider implements PermissionProvider {
 3     
 4     @Override
 5     public Set<String> getUserPermissions(Object userId) {
 6         // 1. 检查是否为超级管理员
 7         if (isSuperAdmin(userId)) {
 8             return getAllPermissions(); // 返回所有权限
 9         }
10         
11         // 2. 查询用户权限(通过用户-角色-菜单关联)
12         List<String> permsList =userService.getPermissions();19         
20         // 3. 返回权限集合
21         return permsList;
27     }
28 }

抽象权限提供者 (AbstractPermissionProvider)

  • 提供模板方法,定义权限获取流程
  • 子类只需实现具体查询逻辑
  • 统一处理超级管理员逻辑
复制代码
 1 public abstract class AbstractPermissionProvider implements PermissionProvider {
 2     
 3     @Override
 4     public Set<String> getUserPermissions(Object userId) {
 5         // 方法:统一处理超管
 6         if (isSuperAdmin(userId)) {
 7             return getAllPermissions();
 8         }
 9         return doGetUserPermissions(userId);
10     }
11     
12     // 子类实现:获取所有权限
13     protected abstract Set<String> getAllPermissions();
14     
15     // 子类实现:查询用户权限
16     protected abstract Set<String> doGetUserPermissions(Object userId);
17 }

Redis存储 (RedisPermissionStorage)

  1. 使用Redis存储用户信息(JSON序列化)
  2. 支持过期时间配置
  3. 支持Key前缀配置
  4. 异常处理和日志记录
复制代码
 1 public class RedisPermissionStorage implements PermissionStorage {
 2     
 3     @Override
 4     public void storeUserInfo(String token, UserInfo userInfo) { 7         redisTemplate.opsForValue().set(key, value, expireSeconds, TimeUnit.SECONDS);
 8     }
 9     
10     @Override
11     public UserInfo getUserInfo(String token) {14         return value;
15     }
16 }

通配符匹配器 (WildcardPermissionMatcher)

  • 支持 *? 通配符
  • 例如:sys:user:* 匹配 sys:user:savesys:user:update
  • 使用Spring的 PatternMatchUtils.simpleMatch()

权限过滤器实现

  1. 请求头提取Token
  2. 从Storage获取用户信息(如不存在,从Provider加载)
  3. 设置到PermissionContext
  4. 请求结束后清理上下文
复制代码
过滤器流程:

请求到达
    ↓
检查排除路径(登录、公开接口等)
    ↓
提取Token
    ↓
从Storage获取用户信息
    ↓(如果不存在)
从Provider加载用户信息 → 存储到Storage
    ↓
设置到PermissionContext
    ↓
继续请求处理
    ↓
清理PermissionContext

权限注解 (@RequirePermission)

  • 支持单个权限、多个权限(AND/OR)
  • 支持角色验证
  • 支持自定义错误消息
复制代码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
    String value() default "";           // 单个权限
    String[] all() default {};            // 全部权限(AND)
    String[] any() default {};            // 任意权限(OR)
    String role() default "";              // 单个角色
    String message() default "权限不足";   // 错误消息
}

AOP切面实现

  1. 拦截 @RequirePermission 注解的方法
  2. 从PermissionContext获取用户信息
  3. 调用PermissionValidator验证权限
  4. 验证失败抛出异常
复制代码
 1 @Aspect
 2 public class PermissionAspect {
 3     
 4     @Before("@annotation(RequirePermission)")
 5     public void checkPermission(JoinPoint joinPoint) {
 6         RequirePermission annotation = getAnnotation(joinPoint);
 7         
 8         // 验证权限
 9         if (!permissionValidator.hasPermission(annotation.value())) {
10             throw new PermissionDeniedException(annotation.message());
11         }
12     }
13 }

与SpringBoot项目集成

配置类设计

  • 自动配置各个组件
  • 支持条件装配(@ConditionalOnMissingBean)
  • 提供默认实现
复制代码
 1 @Configuration
 2 @EnableConfigurationProperties(PermissionProperties.class)
 3 public class PermissionAutoConfiguration {
 4     
 5     @Bean
 6     @ConditionalOnMissingBean
 7     public PermissionMatcher permissionMatcher() {
 8         return new WildcardPermissionMatcher(); 
 9     }
10     
11     @Bean
12     @ConditionalOnMissingBean
13     public PermissionValidator permissionValidator(PermissionMatcher matcher) {
14         return new DefaultPermissionValidator(matcher);
15     }
16     
17     @Bean
18     @ConditionalOnMissingBean
19     public PermissionFilter permissionFilter(...) {
20         return new PermissionFilter(...);
21     }
22 }

使用示例:

复制代码
 1 @RestController
 2 @RequestMapping("/api/user")
 3 public class UserController {
 4     
 5     // 单个权限验证
 6     @GetMapping("/list")
 7     @RequirePermission("sys:user:list")
 8     public ResponseEntity<List<User>> list() {
 9         return ResponseEntity.ok(userService.list());
10     }
11     
12     // 多个权限(全部满足)
13     @PutMapping("/{id}")
14     @RequirePermission(all = {"sys:user:update", "sys:user:edit"})
15     public ResponseEntity<Void> update(@PathVariable Long id, @RequestBody User user) {
16         userService.update(id, user);
17         return ResponseEntity.ok().build();
18     }
19     
20     // 多个权限(任意一个)
21     @DeleteMapping("/{id}")
22     @RequirePermission(any = {"sys:user:delete", "sys:user:remove"})
23     public ResponseEntity<Void> delete(@PathVariable Long id) {
24         userService.delete(id);
25         return ResponseEntity.ok().build();
26     }
27     
28     // 角色验证
29     @GetMapping("/admin")
30     @RequirePermission(role = "ADMIN")
31     public ResponseEntity<String> adminOnly() {
32         return ResponseEntity.ok("Admin only");
33     }
34 }

代码逻辑使用:

复制代码
 1 @Service
 2 public class UserService {
 3     
 4     @Autowired
 5     private PermissionValidator permissionValidator;
 6     
 7     public void deleteUser(Long userId) {
 8         
 9         if (!permissionValidator.hasPermission("sys:user:delete")) {
10             throw new PermissionDeniedException("无删除权限");
11         }
12         
13         userRepository.delete(userId);
14     }
15     
16     public UserContext getCurrentUser() {
17         return permissionValidator.getCurrentUser();
18     }
19 }

总结:

  1. 接口抽象:所有核心功能都通过接口定义,便于扩展
  2. 分层设计:Provider → Storage → Matcher → Validator,职责清晰
  3. 上下文管理:使用ThreadLocal,支持异步场景
  4. 配置化:SQL、路径等可配置,适应不同项目
  5. 默认实现:提供常用实现,开箱即用

按照以上设计思路,可实现一个通用、灵活、易用的权限控制框架。

相关推荐
仙俊红2 小时前
Spring Cloud 核心组件部署方式速查表
后端·spring·spring cloud
码农幻想梦3 小时前
实验九 Restful和ajax实现
后端·ajax·restful
今天多喝热水3 小时前
SpEL(Spring Expression Language) 表达式
java·后端·spring
码农水水3 小时前
浅谈 MySQL InnoDB 的内存组件
java·开发语言·数据库·后端·mysql·面试
独自破碎E3 小时前
Spring Boot的多环境配置
java·spring boot·后端
Edward-tan4 小时前
【玩转全栈】----Django模板语法、请求与响应
后端·python·django
猫头鹰源码(同名B站)4 小时前
基于django+vue的时尚穿搭社区(商城)(前后端分离)
前端·javascript·vue.js·后端·python·django
Watermelo6174 小时前
随机扣款实现赛博共产主义,《明日方舟:终末地》公测支付事故复盘
数据库·后端·游戏程序·技术美术·用户体验·游戏策划·游戏美术
观音山保我别报错4 小时前
Spring Boot 项目学习内容详解(一)
spring boot·后端·学习