在Java中如何实现简单权限校验逻辑_Java权限控制项目讲解

Spring Security 基础权限校验最省事:用 http.authorizeHttpRequests() 配置路径权限,@PreAuthorize 控制方法级权限,统一用 Permission 枚举管理权限标识,JWT 场景需将权限写入 token payload 并自定义 JwtAuthenticationConverter。

用 Spring Security 做基础权限校验最省事

直接上手 Spring Security 是 Java Web 项目里最稳妥的选择,它不强制你写一堆 if-else 判断角色,而是把权限逻辑下沉到配置和注解层。如果你只是想控制“管理员能删、普通用户只能查”,没必要自己造 PermissionService 轮子。

关键点在于:别手动在 Controller 里调 getUserRoles() 再做字符串匹配,那是反模式。

  • 启用 @EnableWebSecurity@Configuration
  • http.authorizeHttpRequests() 配置路径级权限,比如 requestMatchers("/api/users/delete").hasRole("ADMIN")
  • 方法级用 @PreAuthorize("hasRole('ADMIN')")@PreAuthorize("hasAuthority('user:delete')"),注意 hasRole() 会自动补前缀 ROLE_,而 hasAuthority() 不会
  • 确保你的 UserDetails 实现类的 getAuthorities() 返回的是带正确前缀的 GrantedAuthority 对象,不是纯字符串列表

自定义权限表达式需继承 DefaultWebSecurityExpressionHandler

Spring Security 默认不支持像 @PreAuthorize("@permissionService.hasPermission(authentication, 'order:refund')") 这种调用自定义服务的写法,除非你显式替换表达式处理器。

否则你会看到 SpelEvaluationException: EL1008E: Property or field 'permissionService' cannot be found

  • 新建配置类,@Bean 注册一个 DefaultWebSecurityExpressionHandler 实例
  • 调用 setPermissionEvaluator() 绑定你自己的 PermissionEvaluator 实现
  • PermissionEvaluatorhasPermission() 方法里,可安全注入 Authentication、目标对象、权限标识符,做数据库查询或缓存判断
  • 记得在 @EnableGlobalMethodSecurity 中开启 prePostEnabled = true(Spring Boot 2.7+ 改为

    @EnableMethodSecurity

RBAC 模型下避免硬编码权限字符串

"user:read""order:write" 散落在代码各处,后期改权限名或加新资源时极易漏改。应该收口成常量或枚举。

更糟的是有人用数字码(如 101 表示用户查看),完全丧失可读性。

  • 建一个 Permission 枚举,每个值含 code(如 USER_READ)、value(如 "user:read")、desc
  • Controller 方法上写 @PreAuthorize("hasAuthority(T(com.example.auth.Permission).USER_READ.value)")
  • 前端菜单/按钮权限也从同一枚举取 value,保持前后端权限标识一致
  • 数据库表 sys_permissioncode 字段建议与枚举名对齐,方便运维查表
@PreAuthorize("hasAuthority(T(com.example.auth.Permission).ORDER_REFUND.value)")
public ResponseEntity refundOrder(@PathVariable Long orderId) {
    // ...
}

无状态 JWT 场景下权限信息必须塞进 token payload

如果用了 JWT 且不做 session + Redis 存权限,又没把角色或权限列表放进 token,每次请求都得查库——那跟没用 JWT 没区别,还多了签名开销。

常见错误是只存了 userId,然后在拦截器里再查 DB 加载权限,这既破坏无状态,又拖慢响应。

  • 登录成功后,从 DB 查出该用户的全部 GrantedAuthority(即权限字符串),放入 JWT 的 authorities 自定义 claim
  • 写一个 JwtAuthenticationConverter,重写 convert() 方法,从 authorities claim 提取并转成 SimpleGrantedAuthority 列表
  • 确保 token 大小可控:权限粒度别太细(比如不用 user:read:123 这种带 ID 的),否则 token 膨胀快
  • 权限变更后旧 token 不失效,这是 JWT 的固有限制,需要配合短期 token(如 30 分钟)+ refresh token 机制缓解
真正难的不是写校验逻辑,是决定权限模型的粒度和生命周期管理。比如“某个用户临时有某条数据的编辑权”,这种动态授权,光靠 Spring Security 注解搞不定,得结合策略模式或规则引擎。