Spring AOP配置默认切入点后特定请求仍触发默认切面如何解决
问题原因
你当前两个切面的切入点表达式存在包含关系:execution(* com.example.controller.ExampleController.*(..)) 是 execution(* com.example.controller.*.*(..)) 的子集,所以匹配到第一个切入点的请求必然也会匹配第二个切入点。Spring AOP默认会执行所有匹配成功的切面逻辑,因此两个校验方法都会触发。
解决方案
方案1:修改默认切入点表达式,排除已有特定校验的类
直接在默认切面的切入点中排除所有已经定义了单独校验的Controller,是实现成本最低的方式:
// 特定校验切面保留原有逻辑 @Before("execution(* com.example.controller.ExampleController.*(..))") public void validate() { validatePermissions("user", "create"); } // 默认切面新增排除规则,不匹配ExampleController的方法 @Before("execution(* com.example.controller.*.*(..)) && !execution(* com.example.controller.ExampleController.*(..))") public void validateUser() { validatePermissions("admin", "admin"); }
如果后续新增了其他需要特定校验的Controller,只需要在默认切入点的排除规则中新增对应的execution表达式即可。
方案2:通过切面优先级+标记位实现逻辑跳过
如果后续会频繁新增特定校验的Controller,不想每次修改切入点表达式,可以用优先级+ThreadLocal标记的方式实现:
- 给切面加
@Order注解,数值越小优先级越高,让特定校验切面先执行 - 特定校验切面执行后往ThreadLocal中存入校验完成标记
- 默认切面执行前先判断标记是否存在,存在就跳过校验逻辑
代码示例:
// 定义ThreadLocal存储校验标记 private static final ThreadLocal<Boolean> VALIDATED_FLAG = new ThreadLocal<>(); // 特定校验切面优先级设为1(数值越小优先级越高) @Order(1) @Before("execution(* com.example.controller.ExampleController.*(..))") public void validate() { validatePermissions("user", "create"); // 标记已执行过校验 VALIDATED_FLAG.set(Boolean.TRUE); } // 默认校验切面优先级设为2,晚于特定切面执行 @Order(2) @Before("execution(* com.example.controller.*.*(..))") public void validateUser() { try { // 已执行过校验直接跳过 if (VALIDATED_FLAG.get() != null && VALIDATED_FLAG.get()) { return; } validatePermissions("admin", "admin"); } finally { // 请求结束清除标记避免内存泄漏 VALIDATED_FLAG.remove(); } }
方案3:用自定义注解做鉴权规则匹配(更易长期维护)
更推荐的长期方案是用自定义注解标记需要特定鉴权的接口,避免切入点表达式随着业务迭代越来越臃肿:
- 自定义鉴权注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface AuthCheck { String role(); String permission(); }
- 给需要特定校验的Controller/方法加注解
@RestController @AuthCheck(role = "user", permission = "create") public class ExampleController { // 接口业务逻辑 }
- 调整切面逻辑,有注解的用注解配置的规则校验,没有的走默认规则
@Before("execution(* com.example.controller.*.*(..))") public void authValidate(JoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); // 优先读取方法上的注解,没有则读取类上的注解 AuthCheck authCheck = signature.getMethod().getAnnotation(AuthCheck.class); if (authCheck == null) { authCheck = signature.getDeclaringType().getAnnotation(AuthCheck.class); } if (authCheck != null) { // 存在自定义注解走特定校验逻辑 validatePermissions(authCheck.role(), authCheck.permission()); } else { // 无注解走默认校验逻辑 validatePermissions("admin", "admin"); } }
内容的提问来源于stack exchange,提问作者Piyush Tyagi
相关产品推荐
相关产品推荐

