权限校验优先用if-else(Java版本低于14或角色可能为null时),Java 17+且用枚举时可用switch;组合权限应封装方法,避免前端缓存结果;Spring Security中需注意hasRole与hasAuthority前缀差异及SpEL参数引用。

权限校验该用 if-else 还是 switch?
Java 中权限判断本质是多分支逻辑,但 switch 从 Java 14 起才支持 String,而权限常以字符串(如 "ADMIN"、"EDITOR")或枚举(Role.ADMIN)表示。若项目用 Java 17+ 且权限值固定,用 switch 更清晰;否则优先用 if-else if-else,避免 NullPointerException 或 IllegalArgumentException。
常见错误:直接对可能为 null 的角色字段调用 .equals() —— 应先判空或用 Objects.equals(role, "ADMIN")。
- 枚举比字符串更安全:定义
enum Role { ADMIN, EDITOR, VIEWER },编译期可捕获非法值 - 避免硬编码字符串:用常量类(如
public static final String ADMIN = "ADMIN")或配置中心管理权限标识 - 权限字段为空时,默认拒绝(
deny by default),不默认放行
如何安全地组合多个权限条件?
真实场景中权限常需「与」(AND)或「或」(OR)组合,比如「是管理员 或 拥有编辑权限」、「既是部门负责人 且 在当前数据所属部门」。用布尔表达式直接写容易出错,尤其涉及 null 和短路逻辑。
推荐做法:封装成独立方法,明确命名语义,例如:
立即学习“Java免费学习笔记(深入)”;
public boolean canEditPost(User user, Post post) {
if (user == null || post == null) return false;
return user.getRole() == Role.ADMIN
|| (user.getRole() == Role.EDITOR && Objects.equals(user.getDeptId(), post.getDeptId()));
}
- 避免在条件中调用可能抛异常的方法(如
user.getPermissions().contains(...)前未判getPermissions()是否为null) - 复杂组合建议用策略模式或规则引擎(如 Drools),但小项目直接布尔逻辑更轻量
- 注意数据库查询中的权限过滤:不能只靠 Java 层校验,必须在 SQL 中加
WHERE条件,防止越权读取
Spring Security 中的 @PreAuthorize 怎么写才不踩坑?
@PreAuthorize 是声明式权限控制的核心注解,但 SpEL 表达式写错会导致权限始终通过或始终拒绝,且无明显报错。
典型问题:@PreAuthorize("hasRole('ADMIN')") 看似正确,但 Spring Security 默认会把 "ADMIN" 自动转为 "ROLE_ADMIN" —— 如果你的 UserDetails.getAuthorities() 返回的是 SimpleGrantedAuthority("ADMIN"),那这个检查永远失败。
- 统一前缀:要么所有角色都带
ROLE_前缀并用hasRole('ADMIN'),要么用hasAuthority('ADMIN')匹配原始字符串 - 方法参数引用要加
#:如@PreAuthorize("#postId == authentication.principal.departmentId"),漏掉#就变成字面量比较 - 自定义权限表达式需注册
PermissionEvaluator,否则hasPermission()直接抛AccessDeniedException
为什么权限判断结果不能缓存到前端?
有人把 canDelete: true 这类字段随数据一起返回给前端,再由前端控制按钮显隐 —— 这等于放弃服务端校验。用户只要改个 JSON 就能绕过。
真正安全的做法:每次操作请求都走完整权限链路(如 Controller → Service → DAO),并在 DAO 层加租户/部门/状态等维度的 WHERE 条件。前端只做体验优化(如灰掉按钮),不替代后端判断。
容易被忽略的一点:权限依赖的上下文可能变化。例如用户刚被降权,但 token 还没过期,此时仅靠 JWT 中的 roles 字段做判断就不准了——必须查库或调用权限服务实时校验。










