You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Spring Filter中获取路径变量?问题排查与方案问询

解决过滤器中无法获取路径变量的问题

你遇到的核心问题其实是Servlet Filter的执行时机早于DispatcherServlet内部的RequestMappingHandlerMapping——不管怎么调整RequestMappingHandlerMapping的order,过滤器都是在整个Spring MVC请求处理流程之前就运行了,自然拿不到后续才填充的URI_TEMPLATE_VARIABLES_ATTRIBUTE属性。下面给你几个可行的解决方案:

方案一:改用HandlerInterceptor(推荐)

HandlerInterceptor是Spring MVC原生的拦截器,运行在DispatcherServlet的处理流程中,正好是在路径变量解析完成之后执行的,完美适配你的场景。

步骤1:实现HandlerInterceptor

public class SecurityCheckInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 这里可以直接拿到路径变量
        Map<String, String> pathVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
        String uid = pathVariables.get("uid");
        
        // 获取Auth头并解密提取userId
        String authHeader = request.getHeader("Auth");
        String userId = decryptAuthHeader(authHeader); // 替换成你的解密逻辑
        
        // 校验逻辑
        if (!userId.equals(uid)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return false; // 终止请求流程
        }
        return true;
    }
    
    // 你的解密方法
    private String decryptAuthHeader(String authHeader) {
        // 实现解密逻辑
        return "";
    }
}

步骤2:注册拦截器

@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SecurityCheckInterceptor())
                .addPathPatterns("/**"); // 拦截所有需要校验的路径,可按需调整
    }
}

方案二:如果必须使用Filter,手动解析路径变量

如果因为某些原因必须用Filter,你可以借助Spring提供的路径匹配工具,手动从请求路径中解析出uid变量。

示例代码(使用PathPatternMatcher,Spring 5.3+推荐)

@Component
@Order(1) // 调整Filter执行顺序,确保在其他业务Filter之前
public class SecurityFilter implements Filter {

    // 注入Spring的路径匹配器
    private final PathPatternMatcher pathMatcher = new PathPatternMatcher();

    // 提前收集所有包含uid的控制器路径(可以通过ApplicationContext获取)
    private List<PathPattern> uidPaths;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // 从ApplicationContext中获取所有RequestMapping信息
        ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = handlerMapping.getHandlerMethods();
        
        // 提取所有包含{uid}变量的路径
        uidPaths = handlerMethods.keySet().stream()
                .flatMap(info -> info.getPatternsCondition().getPatterns().stream())
                .map(pathMatcher::parse)
                .filter(pattern -> pattern.getVariableNames().contains("uid"))
                .collect(Collectors.toList());
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        String requestPath = httpRequest.getRequestURI();
        
        // 匹配路径并提取uid
        String uid = null;
        for (PathPattern pattern : uidPaths) {
            PathMatchInfo matchInfo = pattern.match(requestPath);
            if (matchInfo != null) {
                uid = matchInfo.getUriVariables().get("uid");
                break;
            }
        }
        
        if (uid == null) {
            // 没有匹配到包含uid的路径,可根据业务决定是否放行
            chain.doFilter(request, response);
            return;
        }
        
        // 后续的Auth头校验逻辑和之前一样
        String authHeader = httpRequest.getHeader("Auth");
        String userId = decryptAuthHeader(authHeader);
        
        if (!userId.equals(uid)) {
            ((HttpServletResponse) response).setStatus(HttpServletResponse.SC_FORBIDDEN);
            return;
        }
        
        chain.doFilter(request, response);
    }
    
    private String decryptAuthHeader(String authHeader) {
        // 你的解密逻辑
        return "";
    }
}

为什么你之前的配置没用?

你设置的RequestMappingHandlerMapping.setOrder(0)是用来调整不同HandlerMapping之间的执行优先级(比如和SimpleUrlHandlerMapping竞争处理请求),但它完全不影响Filter的执行顺序——Filter是Servlet容器层面的组件,在DispatcherServlet处理任何请求之前就会被调用,所以不管怎么调HandlerMapping的顺序,过滤器都拿不到它填充的属性。

内容的提问来源于stack exchange,提问作者Ihor M.

火山引擎 最新活动