我想提供一个基于the answer of @kopelitsa 的解决方案。主要区别在于:
- 通过使用
HandlerExceptionResolver重用控制器异常处理。
- 在 XML 配置上使用 Java 配置
首先,您需要确保您有一个类来处理常规 RestController/Controller 中发生的异常(使用 @RestControllerAdvice 或 @ControllerAdvice 注释的类和使用 @ExceptionHandler 注释的方法)。这会处理您在控制器中发生的异常。下面是一个使用 RestControllerAdvice 的示例:
@RestControllerAdvice
public class ExceptionTranslator {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorDTO processRuntimeException(RuntimeException e) {
return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, "An internal server error occurred.", e);
}
private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) {
(...)
}
}
要在 Spring Security 过滤器链中重用此行为,您需要定义一个过滤器并将其挂钩到您的安全配置中。过滤器需要将异常重定向到上面定义的异常处理。这是一个例子:
@Component
public class FilterChainExceptionHandler extends OncePerRequestFilter {
private final Logger log = LoggerFactory.getLogger(getClass());
@Autowired
@Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
filterChain.doFilter(request, response);
} catch (Exception e) {
log.error("Spring Security Filter Chain Exception:", e);
resolver.resolveException(request, response, null, e);
}
}
}
然后需要将创建的过滤器添加到 SecurityConfiguration。你需要很早就把它挂到链中,因为所有前面的过滤器的异常都不会被捕获。就我而言,在LogoutFilter 之前添加它是合理的。查看默认过滤器链及其顺序in the official docs。这是一个例子:
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private FilterChainExceptionHandler filterChainExceptionHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
(...)
}
}