如何在JwtFilter的doFilter方法中调用UserController的login方法?
你在Filter里用new ModelAndView("user/login")没法触发Controller方法是正常的——ModelAndView是Spring MVC用来封装视图和数据的对象,但Filter运行在DispatcherServlet之前,根本没进入Spring MVC的处理流程,所以这个对象不会被解析执行。下面给你几个靠谱的解决方案:
方案1:使用RequestDispatcher转发请求(最推荐)
这是最符合Servlet规范的方式,直接把请求转发到你的登录接口路径,让DispatcherServlet去处理并调用对应的Controller方法:
if (authHeader == null || !authHeader.startsWith("Bearer ")) { // 转发到/user/login接口 request.getRequestDispatcher("/user/login").forward(req, res); return; // 必须return,避免后续代码继续执行抛出异常 }
这个方案的好处是完全利用Spring MVC的原有流程,不需要额外处理参数解析、返回值渲染等问题,代码也最简洁。
方案2:注入Controller实例直接调用方法
如果你的JwtFilter是由Spring管理的(比如用@Component注解),可以直接注入UserController,然后手动调用它的login方法。不过要注意,Filter里不会自动处理@RequestBody注解,所以需要自己解析请求体:
首先改造JwtFilter:
@Component public class JwtFilter extends GenericFilterBean { private final UserController userController; private final ObjectMapper objectMapper; // 构造器注入(Spring 4.3+支持) public JwtFilter(UserController userController, ObjectMapper objectMapper) { this.userController = userController; this.objectMapper = objectMapper; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String authHeader = request.getHeader("authorization"); if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); chain.doFilter(req, res); } else { if (authHeader == null || !authHeader.startsWith("Bearer ")) { // 手动解析请求体为User对象 User loginUser = objectMapper.readValue(request.getInputStream(), User.class); // 调用Controller的login方法 String loginResult = userController.login(loginUser); // 将结果写入响应 response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(loginResult); response.getWriter().flush(); return; } else { // 原有Token验证逻辑 String token = authHeader.substring(7); try { Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody(); request.setAttribute("claims", claims); } catch (SignatureException e) { throw new ServletException("Invalid token"); } chain.doFilter(req, res); } } } }
这个方案适合你需要对login方法的返回结果做自定义处理的场景,但要注意如果Controller方法有其他Spring注解(比如@Valid),你需要手动处理相关逻辑。
方案3:通过Spring的HandlerMapping调用HandlerMethod
如果需要更灵活的方式(比如不确定具体的Controller),可以利用Spring的RequestMappingHandlerMapping找到对应的处理器方法,然后通过HandlerAdapter执行它:
@Component public class JwtFilter extends GenericFilterBean { private final RequestMappingHandlerMapping handlerMapping; private final HandlerAdapter handlerAdapter; public JwtFilter(RequestMappingHandlerMapping handlerMapping, HandlerAdapter handlerAdapter) { this.handlerMapping = handlerMapping; this.handlerAdapter = handlerAdapter; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; String authHeader = request.getHeader("authorization"); if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); chain.doFilter(req, res); } else { if (authHeader == null || !authHeader.startsWith("Bearer ")) { // 包装请求,指向登录接口 HttpServletRequest loginRequest = new HttpServletRequestWrapper(request) { @Override public String getRequestURI() { return "/user/login"; } @Override public String getMethod() { return "GET"; // 要和Controller的login方法请求方式一致 } }; try { // 找到对应的处理器链 HandlerExecutionChain executionChain = handlerMapping.getHandler(loginRequest); if (executionChain != null) { HandlerMethod handlerMethod = (HandlerMethod) executionChain.getHandler(); // 执行处理器方法 handlerAdapter.handle(loginRequest, response, handlerMethod); } } catch (Exception e) { throw new ServletException("Failed to invoke login controller", e); } return; } else { // 原有Token验证逻辑 String token = authHeader.substring(7); try { Claims claims = Jwts.parser().setSigningKey("secretkey").parseClaimsJws(token).getBody(); request.setAttribute("claims", claims); } catch (SignatureException e) { throw new ServletException("Invalid token"); } chain.doFilter(req, res); } } } }
这个方案最灵活,但代码复杂度也最高,适合复杂场景下的动态调用。
内容的提问来源于stack exchange,提问作者Pranav MS

