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。
立即学习“Java免费学习笔记(深入)”;
- 新建配置类,
@Bean注册一个DefaultWebSecurityExpressionHandler实例 - 调用
setPermissionEvaluator()绑定你自己的PermissionEvaluator实现 - 在
PermissionEvaluator的hasPermission()方法里,可安全注入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_permission的code字段建议与枚举名对齐,方便运维查表
@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()方法,从authoritiesclaim 提取并转成SimpleGrantedAuthority列表 - 确保 token 大小可控:权限粒度别太细(比如不用
user:read:123这种带 ID 的),否则 token 膨胀快 - 权限变更后旧 token 不失效,这是 JWT 的固有限制,需要配合短期 token(如 30 分钟)+ refresh token 机制缓解










