Spring Boot 3.3.2中ServletException无法被/error控制器捕获的问题
问题分析
自定义的AbstractPreAuthenticatedProcessingFilter属于Servlet级别过滤器,执行顺序在Spring MVC的DispatcherServlet之前。当它抛出ServletException时,请求还未进入Spring MVC的处理流程,因此@ControllerAdvice、HandlerExceptionResolver等Spring异常处理机制无法接管,最终由Tomcat的默认错误处理器返回堆栈信息,而非自定义的/error页面。
解决方案
以下是三种可行的解决方式,可根据项目实际情况选择:
1. 在自定义过滤器内手动调用Spring异常处理器
让过滤器通过Spring的HandlerExceptionResolver处理异常,再渲染视图或转发到/error端点:
@Component public class CustomPreAuthFilter extends AbstractPreAuthenticatedProcessingFilter { @Autowired private HandlerExceptionResolver handlerExceptionResolver; @Override protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) { String token = request.getHeader("X-Auth-Token"); if (token == null || !token.equals("valid-token")) { ServletException exception = new ServletException("无效或缺失认证令牌"); try { // 调用Spring异常处理器获取视图 ModelAndView mav = handlerExceptionResolver.resolveException( request, getResponse(), null, exception ); if (mav != null) { // 渲染视图,完成响应处理 mav.getView().render(mav.getModelMap(), request, getResponse()); return null; // 返回null表示已处理异常,终止后续流程 } } catch (Exception e) { // 处理渲染异常 logger.error("处理认证异常失败", e); } // 若处理器未处理,再抛出异常 throw exception; } return token; } }
2. 用全局Servlet Filter捕获前置过滤器异常
注册一个优先级较低的全局Filter,捕获所有前置Filter抛出的异常并转发到/error:
@Component @Order(Ordered.LOWEST_PRECEDENCE - 10) // 确保在自定义PreAuthFilter之后执行 public class FilterExceptionHandler extends GenericFilterBean { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(request, response); } catch (Exception e) { // 转发到Spring的error端点,交由Spring处理 ((HttpServletRequest) request).getRequestDispatcher("/error").forward(request, response); } } }
3. 结合Spring Security的异常处理机制(若使用Spring Security)
如果项目依赖Spring Security,可通过配置AuthenticationEntryPoint来处理认证失败的异常,直接转发到自定义错误页面:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http, CustomPreAuthFilter preAuthFilter) throws Exception { http .addFilterBefore(preAuthFilter, UsernamePasswordAuthenticationFilter.class) .exceptionHandling(config -> config.authenticationEntryPoint((request, response, authException) -> { // 转发到自定义error页面 request.getRequestDispatcher("/error").forward(request, response); }) ); return http.build(); }
注意:此时建议在过滤器中抛出Spring Security的AuthenticationException子类(如BadCredentialsException),而非ServletException,以便Security的异常处理机制识别。
额外注意事项
- 确保自定义的
/error页面能被Spring Boot识别:可放在src/main/resources/templates/error.html(模板引擎场景)或src/main/resources/static/error.html(静态资源场景),也可通过实现ErrorController自定义错误逻辑。 - 自定义过滤器需被Spring上下文管理:通过
@Component或@Bean注解注册,确保能注入Spring的Bean(如HandlerExceptionResolver)。
内容的提问来源于stack exchange,提问作者Steve Maring
相关产品推荐
相关产品推荐

