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

如何编写可获取请求控制器方法的Spring Boot请求过滤器?

如何编写可访问控制器处理方法的Spring Boot过滤器?

我已将自定义AccessTokenAuthFilter添加到Spring Boot应用的组件扫描路径中,但请求运行时,过滤器虽有部分属性,却无法获取匹配的控制器HandlerMethod。尝试通过HandlerMethod handlerMethod = (HandlerMethod) req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);获取时返回null。调试显示过滤器链有4个条目且已确保该过滤器最后执行,但此时请求属性中无HandlerMethod相关内容,请问如何编写可访问控制器处理方法的过滤器?

package io.xj.access;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.filter.OrderedFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Objects;

@Component
public class AccessTokenAuthFilter implements OrderedFilter {
  private final Logger LOG = LoggerFactory.getLogger(io.xj.hub.access.HubAccessTokenAuthFilter.class);

  // Order defaults to after Spring Session filter
  private final int order = REQUEST_WRAPPER_FILTER_MAX_ORDER - 10;

  @Override
  public void doFilter(ServletRequest servletReq, ServletResponse servletRes, FilterChain chain)
    throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) servletReq;
    HttpServletResponse res = (HttpServletResponse) servletRes;

    HandlerMethod handlerMethod = (HandlerMethod) req.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE);

    if (Objects.nonNull(handlerMethod)) {
      Method method = handlerMethod.getMethod();

      // use reflection to get resource method annotation values
      RolesAllowed aRolesAllowed = method.getAnnotation(RolesAllowed.class);
      PermitAll aPermitAll = method.getAnnotation(PermitAll.class);
      DenyAll aDenyAll = method.getAnnotation(DenyAll.class);

      // todo decide whether we are authenticated
    }

    chain.doFilter(req, res);
  }

  @Override
  public int getOrder() {
    return this.order;
  }
}

问题核心

Servlet Filter是Servlet容器层面的组件,执行时机早于Spring MVC的HandlerMapping映射流程——哪怕把Filter设为最后执行,也只是在容器的Filter链中最后运行,此时Spring还未完成请求到控制器方法的匹配,自然拿不到HandlerMethod。

解决方案:改用Spring MVC HandlerInterceptor

HandlerInterceptor是Spring MVC框架内的组件,在HandlerMapping完成请求映射后执行,能直接获取到HandlerMethod实例,完美适配你的场景。

1. 实现HandlerInterceptor

package io.xj.access;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Objects;

public class AccessTokenAuthInterceptor implements HandlerInterceptor {
    private final Logger LOG = LoggerFactory.getLogger(AccessTokenAuthInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 仅处理控制器方法,排除静态资源等非控制器请求
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            // 获取权限注解
            RolesAllowed aRolesAllowed = method.getAnnotation(RolesAllowed.class);
            PermitAll aPermitAll = method.getAnnotation(PermitAll.class);
            DenyAll aDenyAll = method.getAnnotation(DenyAll.class);

            // 示例:遇到DenyAll直接返回403
            if (Objects.nonNull(aDenyAll)) {
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                return false;
            }

            // 在这里实现你的权限校验逻辑
        }
        // 返回true继续执行后续流程,返回false终止请求
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 可选:请求处理完成后执行的逻辑
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 可选:请求全流程结束后执行的逻辑
    }
}

2. 注册Interceptor到Spring MVC

创建配置类,将自定义Interceptor纳入Spring MVC的拦截器链:

package io.xj.access;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,指定拦截路径(此处拦截所有请求)
        registry.addInterceptor(new AccessTokenAuthInterceptor())
                .addPathPatterns("/**")
                // 可选:排除不需要拦截的路径,比如登录接口、静态资源
                .excludePathPatterns("/login", "/static/**");
    }
}

内容的提问来源于stack exchange,提问作者Nick Charney Kaye

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 18:32:46