如何在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.




