问题
I am building a REST API that uses Spring Security (and it's filter chain) to authenticate the user via JWT. Now, if such a JWT is missing, expired or similar, I would like to return a nicely formatted error message to the API consumer, instead of the default whitelabel error response. The API error messages returned out of the Spring Security filter should look identical to the ones returned in case of business logic failure.
In case of business logic failure, my Spring REST controllers return error messages that are formatted like this (via @RestControllerAdvice
):
Content-Type: application/json
{
"code": "VOUCHER_NOT_FOUND",
"message": "The specified voucher code was not found.",
"timestamp": "2020-09-06T21:22:23.015Z"
}
I understand that if an error happens in the Spring Security filter chain, controllers will never be reached, so I have to return an HTTP error message out of the security filter. I've tried to do this like this:
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
@Override
protected final void doFilterInternal(final HttpServletRequest request,
final HttpServletResponse response, final FilterChain chain)
throws IOException, ServletException {
try {
// Perform various auth checks here
// Throw JwtAuthorizationException if a check fails
chain.doFilter(request, response);
} catch (JwtAuthorizationFailedException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.setHeader("Content-Type", "application/json"); // does not work
response.getWriter().write("{ \"Simple\": \"Test\" }");
}
}
The problem is, that the resulting error message I get, always sets a different Content-Type
header (with charset=ISO-8859-1
added):
Content-Type: application/json;charset=ISO-8859-1
{
"Simple": "Test"
}
I would like to streamline this and make it consistent. So the question is, how can I make sure, only
Content-Type: application/json
is returned from the security filter? I've tried numerous options, like
response.setHeader("Content-Type", "application/json");
or
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
but all of them did not work. Any ideas?
回答1:
The problem in this case comes from getWriter()
method:
This one includes the default character encoding into the Response
that will be returned and, as you can see in the next picture, it provokes the "additional information" inside the returned Content-Type
.
When Spring serialize the response, uses "getter methods" and, as you can see, getContentType
includes the current charset
. That is the reason you see that one besides desired Content-Type
value.
Even if you try to set charset
with a null
value, it won't work because the method will detect you are using a Writer
and it won't be changed (see next picture)
However, there is a way to achieve what you want:
} catch (JwtAuthorizationFailedException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getOutputStream().print("{ \"Simple\": \"Test\" }");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
}
Use getOutputStream().print
instead of getWriter().write
来源:https://stackoverflow.com/questions/64348699/return-custom-http-error-message-from-spring-security-filter