如何编写可获取请求控制器方法的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
相关产品推荐
相关产品推荐

