Return custom http error message from Spring Security filter

萝らか妹 提交于 2021-02-10 23:20:50

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!