Issue
In my Spring Boot Web application I am throwing a org.springframework.security.access.AccessDeniedException
in my business code whenever a user does not have a necessary authority. The AccessDeniedException
has the advantage that Spring automatically calls the AuthenticationEntryPoint etc.
However, I am using a custom exception message to indicate the exact cause of the exception (e.g. throw new AccessDeniedException("You do not have permissions to do XYZ")
, but in the generated JSON response the message is always "Access Denied", e.g.:
{
"timestamp": "2019-12-12T10:01:10.342+0000",
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/myurl"
}
Digging into the code of Spring (DefaultErrorAttributes
method addErrorDetails
) reveals that the message is built from 1) the exception message 2) the request attribute javax.servlet.error.message
, with the second overriding the first.
Does anybody know why the request attribute has priority over the exception message?
And is there an easy way to display the custom exception message within the Spring Boot error response? I would like to retain the default Spring Boot error response, but with my custom message in the "message" field and ideally I want Spring Boot to construct the rest of the JSON object (i.e. timestamp, status, etc). The final result should look something like this:
{
"timestamp": "2019-12-12T10:01:10.342+0000",
"status": 403,
"error": "Forbidden",
"message": "You do not have permissions to do XYZ",
"path": "/myurl"
}
This does work for other kinds of exceptions. Just AccessDeniedException seems to be a special case. Is AccessDeniedException maybe not the right choice for this rqeuirement (although the javadoc suggests it is)?
Solution
You could use Springs ControllerAdvice and send a custom error using the HttpServletResponse
:
@ControllerAdvice
class AccessDeniedExceptionHandler {
@ExceptionHandler(value = AccessDeniedException.class)
public void handleConflict(HttpServletResponse response) throws IOException {
response.sendError(403, "Your Message");
}
}
This will return:
{
"timestamp": 1576248996366,
"status": 403,
"error": "Forbidden",
"message": "Your Message",
"path": "/foo/bar"
}
This way you can globally catch all Exceptions of a specific type and make your controller return a specified custom value. You could do so as well e.g. for any IOException
and return a 404 Not Found ResponseEntity
with a message you specify.
Answered By - T A
Answer Checked By - Timothy Miller (JavaFixing Admin)