Issue
I'm using Spring MVC's @ControllerAdvice
and @ExceptionHandler
to handle all the exception of a REST Api. It works fine for exceptions thrown by web mvc controllers but it does not work for exceptions thrown by spring security custom filters because they run before the controller methods are invoked.
I have a custom spring security filter that does a token based auth:
public class AegisAuthenticationFilter extends GenericFilterBean {
...
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
try {
...
} catch(AuthenticationException authenticationException) {
SecurityContextHolder.clearContext();
authenticationEntryPoint.commence(request, response, authenticationException);
}
}
}
With this custom entry point:
@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authenticationException.getMessage());
}
}
And with this class to handle exceptions globally:
@ControllerAdvice
public class RestEntityResponseExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler({ InvalidTokenException.class, AuthenticationException.class })
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ResponseBody
public RestError handleAuthenticationException(Exception ex) {
int errorCode = AegisErrorCode.GenericAuthenticationError;
if(ex instanceof AegisException) {
errorCode = ((AegisException)ex).getCode();
}
RestError re = new RestError(
HttpStatus.UNAUTHORIZED,
errorCode,
"...",
ex.getMessage());
return re;
}
}
What I need to do is to return a detailed JSON body even for spring security AuthenticationException. Is there a way make spring security AuthenticationEntryPoint and spring mvc @ExceptionHandler work together?
I'm using spring security 3.1.4 and spring mvc 3.2.4.
Solution
Ok, I tried as suggested writing the json myself from the AuthenticationEntryPoint and it works.
Just for testing I changed the AutenticationEntryPoint by removing response.sendError
@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint{
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
response.setContentType("application/json");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getOutputStream().println("{ \"error\": \"" + authenticationException.getMessage() + "\" }");
}
}
In this way you can send custom json data along with the 401 unauthorized even if you are using Spring Security AuthenticationEntryPoint.
Obviously you would not build the json as I did for testing purposes but you would serialize some class instance.
In Spring Boot, you should add it to http.authenticationEntryPoint() part of SecurityConfiguration file.
Answered By - Nicola
Answer Checked By - Mary Flores (JavaFixing Volunteer)