I have a Spring Boot app using Jersey as the JAX-RS implementation. This is my security configuration:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired TokenAuthenticationProvider tokenAuthenticationProvider;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(tokenAuthenticationProvider);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(new AuthenticationTokenFilter(), BasicAuthenticationFilter.class)
.csrf().disable()
.authorizeRequests()
.antMatchers("/dataHub/**")
.authenticated();
}
}
What I want to be able to do is to have a way to catch the Exceptions thrown by my TokenAuthenticationProvider and convert them into a standardized JSON format that we have agreed upon. Is there a way to do this? I tried messing around with adding a custom AuthenticationFailureHandler, but couldn't get that to work.
WebSecurityConfigurerAdapter appraoch
The HttpSecurity class has a method called exceptionHandling which can be used to override the default behavior. The following sample presents how the response message can be customized.
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// your custom configuration goes here
.exceptionHandling()
.authenticationEntryPoint((request, response, e) -> {
String json = String.format("{\"message\": \"%s\"}", e.getMessage());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().write(json);
});
}
@ControllerAdvice appraoch - Why it doesn't work in this case
At first I thought about @ControllerAdvice that catches authentication exceptions for the entire application.
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
@ControllerAdvice
public class AuthExceptionHandler {
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AuthenticationException.class)
@ResponseBody
public String handleAuthenticationException(AuthenticationException e) {
return String.format("{\"message\": \"%s\"}", e.getMessage());
}
}
In the example above, the JSON is built manually, but you can simply return a POJO which will be mapped into JSON just like from a regular REST controller. Since Spring 4.3 you can also use @RestControllerAdvice, which is a combination of @ControllerAdvice and @ResponseBody.
However, this approach doesn't work because the exception is thrown by the AbstractSecurityInterceptor and handled by ExceptionTranslationFilter before any controller is reached.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With