Spring Boot中TokenExpiredException未被ControllerAdvice捕获如何解决
问题根因
- Spring MVC 的
@ControllerAdvice全局异常处理器仅能捕获从Controller层抛出的异常,而JWT校验的JWTValidarFilter属于Servlet过滤器,执行顺序远早于请求到达DispatcherServlet和Controller,过滤器中抛出的异常不会进入ControllerAdvice的处理逻辑,所以才会返回通用未处理错误。
解决方法
方法1:在Filter内直接处理异常
修改doFilterInternal方法,增加异常捕获逻辑,匹配到TokenExpiredException时直接构造响应返回,无需走全局异常流程:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String atributo = request.getHeader(HEADER_ATRIBUTO); if(atributo == null) { chain.doFilter(request, response); return; } if(!atributo.startsWith(ATRIBUTO_PREFIXO)) { chain.doFilter(request, response); return; } String token = atributo.replace(ATRIBUTO_PREFIXO, ""); try { UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(token); SecurityContextHolder.getContext().setAuthentication(authenticationToken); chain.doFilter(request, response); } catch (TokenExpiredException e) { // 直接构造符合要求的响应 response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); ObjectMapper mapper = new ObjectMapper(); Map<String, Object> resMap = new HashMap<>(); resMap.put("timestamp", new Date()); resMap.put("message", e.getMessage()); resMap.put("details", request.getRequestURI()); response.getWriter().write(mapper.writeValueAsString(resMap)); } }
方法2:使用Spring Security原生认证异常处理入口
自定义AuthenticationEntryPoint统一处理所有Security层面的认证异常,可复用你全局异常的响应结构:
- 自定义异常处理器:
@Component public class CustomAuthEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { response.setContentType("application/json;charset=utf-8"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); ObjectMapper mapper = new ObjectMapper(); Map<String, Object> resMap = new HashMap<>(); resMap.put("timestamp", new Date()); // 取出Filter中抛出的原始异常 String errMsg = authException.getCause() != null ? authException.getCause().getMessage() : authException.getMessage(); resMap.put("message", errMsg); resMap.put("details", request.getRequestURI()); response.getWriter().write(mapper.writeValueAsString(resMap)); } }
- 在Spring Security配置类中注册该处理器:
@Autowired private CustomAuthEntryPoint customAuthEntryPoint; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .anyRequest().authenticated() .and() // 配置自定义认证异常入口 .exceptionHandling() .authenticationEntryPoint(customAuthEntryPoint) .and() .addFilter(new JWTValidarFilter(authenticationManager())); // 其余原有配置保持不变 }
修改完成后传入过期Token测试,即可返回你定义的401状态码和对应响应结构。
内容的提问来源于stack exchange,提问作者sauloRicardo
相关产品推荐
相关产品推荐

