Spring Boot 3.0迁移后@ControllerAdvice全局异常处理器未生效
Spring Boot 3.0迁移后自定义异常处理及认证失败响应问题解决
一、自定义GlobalExceptionHandler未生效,内置异常返回标准Problem Details
问题原因
- Spring Boot 3.0默认启用RFC 7807 Problem Details规范,内置的
ProblemDetailsExceptionHandler会优先处理部分内置异常(如类型转换错误TypeMismatchException),导致自定义ResponseEntityExceptionHandler的逻辑未触发。 - 你的
GlobalExceptionHandler仅实现了FileAccessException的处理,未覆盖ResponseEntityExceptionHandler中针对内置异常的处理方法(比如handleTypeMismatch),因此这类异常仍走默认流程。
解决方案
- 重写
ResponseEntityExceptionHandler中的内置异常处理方法
针对你遇到的类型转换错误,重写对应方法并添加自定义字段:
@ControllerAdvice @Slf4j public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { private static ErrorResponse mapToErrorResponse(Exception e, ApplicationError applicationError, String errorMessage, HttpStatus status) { return ErrorResponse.builder(e, status, errorMessage) .property("code", String.valueOf(applicationError.getCode())) .property("timestamp", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(Instant.now())) .build(); } @ExceptionHandler(FileAccessException.class) public ErrorResponse fileAccessException(FileAccessException fae) { log.error("File access Exception", fae); return mapToErrorResponse(fae, fae.getApplicationError(), fae.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } // 重写类型转换异常处理逻辑 @Override protected ResponseEntity<Object> handleTypeMismatch(TypeMismatchException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { // 自定义错误码,可根据业务场景调整 ApplicationError error = ApplicationError.of(24, "参数类型不匹配"); ErrorResponse errorResponse = ErrorResponse.builder(ex, status, ex.getMessage()) .property("code", String.valueOf(error.getCode())) .property("timestamp", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(Instant.now())) .build(); return handleExceptionInternal(ex, errorResponse, headers, status, request); } // 按需重写其他内置异常处理方法,如handleMethodArgumentNotValid、handleHttpMessageNotReadable等 }
- 全局配置调整(可选)
若需完全禁用默认Problem Details自动配置(不推荐,建议通过重写方法扩展),可在启动类中排除相关配置:
@SpringBootApplication(exclude = ProblemDetailsAutoConfiguration.class) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
二、UrlAuthenticationFailureHandler响应体为空
问题原因
Spring Security的认证失败处理独立于Spring MVC错误机制,server.error相关配置仅对MVC层面异常生效,无法控制认证失败的响应输出。默认UrlAuthenticationFailureHandler仅返回状态码,不会写入响应体。
解决方案
自定义AuthenticationFailureHandler,主动写入JSON响应:
@Component public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpStatus.UNAUTHORIZED.value()); // 构建自定义响应体 Map<String, Object> errorResponse = new HashMap<>(); errorResponse.put("type", "about:blank"); errorResponse.put("title", "Unauthorized"); errorResponse.put("status", HttpStatus.UNAUTHORIZED.value()); errorResponse.put("detail", exception.getMessage()); errorResponse.put("instance", request.getRequestURI()); errorResponse.put("timestamp", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(Instant.now())); errorResponse.put("code", 40101); // 自定义认证失败错误码 new ObjectMapper().writeValue(response.getWriter(), errorResponse); } }
在Spring Security配置中注入并使用该处理器:
@Configuration @EnableWebSecurity public class SecurityConfig { private final CustomAuthenticationFailureHandler customAuthenticationFailureHandler; public SecurityConfig(CustomAuthenticationFailureHandler customAuthenticationFailureHandler) { this.customAuthenticationFailureHandler = customAuthenticationFailureHandler; } @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .formLogin(form -> form.failureHandler(customAuthenticationFailureHandler)) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> customAuthenticationFailureHandler.onAuthenticationFailure(request, response, authException) ) ); return http.build(); } }
内容的提问来源于stack exchange,提问作者César Castro Aroche
相关产品推荐
相关产品推荐

