Java方法级权限校验通过自定义注解+Spring AOP实现:定义@RequirePermission注解标记方法,编写@Aspect切面在执行前读取注解、获取当前用户权限并校验,匹配后放行,否则抛AccessDeniedException。

Java 中实现方法级权限校验拦截,核心是结合自定义注解 + AOP(面向切面编程)或 Spring 的 @Aspect,在目标方法执行前检查调用方是否具备指定权限。关键不在注解本身(它只是标记),而在于“谁来读取它”和“读到后做什么”。
1. 定义权限校验注解
创建一个运行时保留的自定义注解,用于标记需要权限控制的方法:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequirePermission {
String value() default ""; // 如 "user:delete"
String[] permissions() default {}; // 支持多个权限
}说明:
- @Target(METHOD) 表示只能用在方法上;
- @Retention(RUNTIME) 是必须的,否则运行时无法通过反射获取;
- permissions() 比 value() 更灵活,建议优先使用数组形式。
2. 编写切面进行权限拦截
用 @Aspect 切面在方法执行前读取注解并校验权限:
@Aspect
@Component
public class PermissionCheckAspect {
<pre class="brush:php;toolbar:false;">@Around("@annotation(requirePermission)")
public Object checkPermission(ProceedingJoinPoint joinPoint,
RequirePermission requirePermission) throws Throwable {
// 1. 获取当前用户(通常从 SecurityContext、ThreadLocal 或 JWT 中提取)
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || !auth.isAuthenticated()) {
throw new AccessDeniedException("用户未登录");
}
// 2. 提取注解中声明的所需权限
Set<String> requiredPerms = Set.of(requirePermission.permissions());
if (requiredPerms.isEmpty()) {
requiredPerms = Set.of(requirePermission.value());
}
// 3. 获取用户实际拥有的权限(例如从 UserDetails 或数据库查)
Collection<? extends GrantedAuthority> authorities = auth.getAuthorities();
Set<String> userPerms = authorities.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toSet());
// 4. 校验:至少有一个权限匹配(或按需改成“全部满足”)
boolean hasPermission = requiredPerms.stream()
.anyMatch(userPerms::contains);
if (!hasPermission) {
throw new AccessDeniedException("权限不足:" + requiredPerms);
}
// 5. 放行
return joinPoint.proceed();
}}
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
注意:
- 确保该类被 Spring 扫描到(加 @Component)且启用了 AOP(@EnableAspectJAutoProxy);
- 权限数据来源要与你的认证体系对齐(如 Spring Security 的 UserDetails、自定义 token 解析等);
- 可根据业务需要扩展逻辑:支持 Ant 风格路径匹配、角色+权限组合、动态权限(查 DB)、忽略特定环境等。
3. 在 Controller 或 Service 方法上使用
直接标注即可,无需改业务逻辑:
@PostMapping("/users/{id}")
@RequirePermission(permissions = {"user:delete", "admin:all"})
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.ok().build();
}也可以简化写法:@RequirePermission("user:update") —— 依赖注解中 value() 的默认行为。
4. 补充建议与常见问题
- 异常统一处理:用 @ControllerAdvice 捕获 AccessDeniedException,返回 403 JSON 响应;
- 性能考虑:权限检查本身轻量,但若频繁查 DB,建议缓存用户权限(如登录后加载进 Authentication);
- 与 Spring Security 兼容:本方案可作为补充(如细粒度方法权限),不替代 @PreAuthorize,但避免混用造成语义混乱;
- 非 Spring 环境:可用 Java Agent + 字节码增强(如 ByteBuddy),但复杂度高,一般项目不推荐。

















