I have a backend protected by spring security configured with antMatchers()
like .antMatchers("/api/myProtectedUri/*").authenticated()
In the same app I use @ControllerAdvice
and @ExceptionHandler
to handle exceptions thrown. To protect the API from returning uncontrolled responses from backend (like stacktraces) I wrote something like this:
@ExceptionHandler({RuntimeException.class})
public ResponseEntity handleRuntimeException() {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Unexpected error occurred");
}
This caused test failures, because the handler probably catches also the exceptions thrown by spring security while protecting endpoints, there is status code 500 (from handler) instead of 401 or 403 expected (from spring)
I have tried to use custom handler like:
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
It is still caught by the @ExceptionHandler with RuntimeException clause.
My questions are:
Spring security is based on filter, and Spring MVC is based on servlet, The @ExceptionHandler
should not catch the exception that spring security throws.
.and()
.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler())
This is an ExceptionTranslationFilter
, it will handle the AuthenticationException
and AccessDeniedException
, source code see here
Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(var10);
RuntimeException ase = (AuthenticationException)this.throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, causeChain);
if (ase == null) {
ase = (AccessDeniedException)this.throwableAnalyzer.getFirstThrowableOfType(AccessDeniedException.class, causeChain);
}
if (ase == null) {
if (var10 instanceof ServletException) {
throw (ServletException)var10;
}
if (var10 instanceof RuntimeException) {
throw (RuntimeException)var10;
}
throw new RuntimeException(var10);
}
if (response.isCommitted()) {
throw new ServletException("Unable to handle the Spring Security Exception because the response is already committed.", var10);
}
this.handleSpringSecurityException(request, response, chain, (RuntimeException)ase);
AuthenticationException
(usually means not authenticated) will be handled by AuthenticationEntryPoint
, you can implement your own AuthenticationEntryPoint
, just overwrite the commence
method, eg HttpStatusEntryPoint
, it just return response with customer status.
public final class HttpStatusEntryPoint implements AuthenticationEntryPoint {
private final HttpStatus httpStatus;
public HttpStatusEntryPoint(HttpStatus httpStatus) {
Assert.notNull(httpStatus, "httpStatus cannot be null");
this.httpStatus = httpStatus;
}
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setStatus(this.httpStatus.value());
}
}
and you can set the entry point to the exception handling.
exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
AccessDeniedHandler
(usually means not authorized) will be handled by AccessDeniedHandler
which you have done.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.