权限校验应放在独立组件层,如自定义注解+AOP或Spring Security的@PreAuthorize;避免在Controller中硬写if,需用枚举管理角色、封装校验器、集中资源维度判断逻辑。

权限校验该放在哪一层?别在Controller里硬写if
直接在 @RestController 方法里堆 if (user.getRole() != ADMIN) 看似快,但会导致权限逻辑散落、无法复用、测试困难。真实项目里应抽到独立组件,比如自定义注解 + AOP,或 Spring Security 的 @PreAuthorize。
如果你只是做原型、脚手架或内部小工具,又不想引入 Spring Security,可以封装一个轻量校验器:
public class SimpleAuthChecker {
public static boolean hasPermission(User user, String requiredRole) {
return user != null
&& user.getRoles() != null
&& user.getRoles().contains(requiredRole);
}
}
- 不要直接比对字符串字面量(如
"ADMIN"),用常量或枚举更安全 - 注意
user.getRoles()返回的是List还是Set—— 含重复角色时contains行为一致,但性能上Set更稳 - 空值必须显式判断,否则 NPE 会出现在运行时,且难以定位到权限环节
用枚举管理角色比字符串靠谱得多
用字符串写角色名("admin", "editor")容易拼错、难重构、IDE 不提示。换成枚举后,校验逻辑可读性与健壮性都提升:
public enum UserRole {
ADMIN, EDITOR, VIEWER;
public boolean implies(UserRole other) {
switch (this) {
case ADMIN: return true;
case EDITOR: return other == EDITOR || other == VIEWER;
case VIEWER: return other == VIEWER;
default: return false;
}
}
}
- 把
User类里的role字段类型从String改成UserRole,反序列化时加@JsonCreator或 Jackson 枚举配置 - 避免用
ordinal()做权限推导——一旦调整枚举顺序就崩 - 如果需要多角色(如用户同时是 EDITOR 和 VIEWER),就别用单个枚举字段,改用
Set
Spring Boot 中用 @PreAuthorize 最省事也最易翻车
加了 Spring Security 依赖后,@PreAuthorize("hasRole('ADMIN')") 看起来一行解决,但实际踩坑点不少:
立即学习“Java免费学习笔记(深入)”;
- 默认要求角色名带
"ROLE_"前缀,即hasRole('ADMIN')实际查的是"ROLE_ADMIN";要么改用户角色数据,要么用hasAuthority('ADMIN') -
@PreAuthorize在代理对象上才生效——非 Controller Bean(比如 Service 内部调用)可能不触发 - 表达式里不能直接访问方法参数名,除非编译时加
-parameters,否则得用#id或#p0这种位置引用 - 异常默认抛
AccessDeniedException,需配@ControllerAdvice统一转成 403 JSON 响应,否则前端只看到白页
没用框架时如何让 if 判断不变成屎山
真要手写条件控制,核心是把“谁有权限”和“有没有权限”拆开。例如:
if (!PostPermissionChecker.canEdit(post, currentUser)) {
throw new ForbiddenException("No edit access to post " + post.getId());
}
而 PostPermissionChecker 内部按资源维度判断:
- 是否作者(
post.getAuthorId().equals(currentUser.getId())) - 是否管理员(
currentUser.hasRole(UserRole.ADMIN)) - 是否在编辑窗口期内(
post.getCreatedAt().isAfter(Instant.now().minusSeconds(300)))
这种写法把权限规则集中、可测、可加日志,比在业务逻辑里塞七八个 if 清晰得多。复杂权限系统迟早要走向策略模式或 Drools,但起步阶段,先管住“判断分散”这个最大破口。
真正麻烦的从来不是写一个 if,而是下次加个“部门可见范围”时,你发现所有地方都要补三行新条件,且没人记得清上次改在哪一行。










