You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Spring AOP切面在控制器参数验证前执行?

问题:如何让权限校验切面在请求体验证前执行?

现有代码中,控制器方法通过@Valid注解做请求体验证,同时用@AllowedScopes注解配合切面做权限校验,但目前切面逻辑会在请求体验证通过后才执行,导致请求体验证失败时先返回验证错误,权限不足时才返回403。需要调整逻辑,让权限校验在请求体绑定和验证之前执行。

原代码如下:

class OrderController {
    @AllowedScopes({ORDER_CREATE})
    @PostMapping("/create")
    public CreateOrderResponse createOrder(@Valid @RequestBody OrderRequest request){
    }
}

@Aspect
@Component
public class AllowedScopeAspect {
    @Pointcut("@annotation(allowedScopes)")
    private void callAtAllowedScopes(AllowedScopes allowedScopes) {
        // just a pointcut signature
    }

    @Before(value = "callAtAllowedScopes(allowedScopes)", argNames = "jp,allowedScopes")
    public void validateScope(JoinPoint jp, AllowedScopes allowedScopes) {
         ...
    }
}

解决方案

方法1:改用Spring MVC拦截器(推荐)

Spring MVC的拦截器preHandle方法会在请求进入控制器方法、完成参数绑定和验证之前触发,刚好符合权限校验前置的需求。

  1. 自定义拦截器实现权限校验:
@Component
public class ScopeValidationInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 仅处理控制器方法的请求
        if (handler instanceof HandlerMethod handlerMethod) {
            AllowedScopes allowedScopes = handlerMethod.getMethodAnnotation(AllowedScopes.class);
            if (allowedScopes != null) {
                // 执行你的权限校验逻辑
                String[] requiredScopes = allowedScopes.value();
                boolean hasPermission = checkUserScopes(requiredScopes); // 替换为实际校验逻辑
                if (!hasPermission) {
                    response.setStatus(HttpStatus.FORBIDDEN.value());
                    return false;
                }
            }
        }
        return true;
    }

    // 示例权限校验方法
    private boolean checkUserScopes(String[] requiredScopes) {
        // 这里实现获取用户当前权限并和所需权限对比的逻辑
        return false;
    }
}
  1. 注册拦截器到Spring MVC配置:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final ScopeValidationInterceptor scopeValidationInterceptor;

    // 构造注入拦截器
    public WebMvcConfig(ScopeValidationInterceptor scopeValidationInterceptor) {
        this.scopeValidationInterceptor = scopeValidationInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(scopeValidationInterceptor)
                // 配置需要拦截的路径,比如所有接口请求
                .addPathPatterns("/**");
    }
}

方法2:调整切面切入点,拦截更早的执行阶段

如果坚持使用AspectJ切面,可以把切入点定位到Spring MVC处理请求的核心方法,这样能在参数解析和验证前触发权限校验。

修改后的切面代码:

@Aspect
@Component
public class AllowedScopeAspect {

    // 切入点定位到RequestMappingHandlerAdapter的invokeHandlerMethod方法
    @Pointcut("execution(* org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(..))")
    private void invokeHandlerMethod() {}

    @Before("invokeHandlerMethod()")
    public void validateScope(JoinPoint joinPoint) {
        // 从方法参数中获取HandlerMethod(控制器方法的封装对象)
        Object[] args = joinPoint.getArgs();
        HandlerMethod handlerMethod = (HandlerMethod) args[0];
        
        // 检查当前控制器方法是否标注了@AllowedScopes
        AllowedScopes allowedScopes = handlerMethod.getMethodAnnotation(AllowedScopes.class);
        if (allowedScopes != null) {
            // 执行权限校验逻辑
            boolean hasPermission = checkUserScopes(allowedScopes.value()); // 替换为实际校验逻辑
            if (!hasPermission) {
                throw new AccessDeniedException("无权限执行该操作");
            }
        }
    }

    // 示例权限校验方法
    private boolean checkUserScopes(String[] requiredScopes) {
        // 实现具体的权限对比逻辑
        return false;
    }
}

原切面延迟执行的原因

原切面的切入点@annotation(allowedScopes)默认拦截的是控制器方法的执行阶段,而Spring MVC的@Valid参数验证是在控制器方法执行前的参数解析环节完成的,所以切面逻辑会在验证之后触发。

内容的提问来源于stack exchange,提问作者Sarvar Nishonboyev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:03:21