英文:
Return custom http error message from Spring Security filter
问题
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.setContentType("application/json"); // Set the Content-Type header
response.setCharacterEncoding("UTF-8"); // Set the character encoding
String errorMessage = "{ \"Simple\": \"Test\" }";
response.getWriter().write(errorMessage);
}
}
}
英文:
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
得分: 3
在这种情况下的问题来自于getWriter()
方法:
这个问题在返回的Response
中包含了默认的字符编码,正如你在下一个图片中看到的那样,它引发了在**返回的Content-Type
**内的“附加信息”。
当Spring对响应进行序列化时,它使用“getter方法”,正如你所看到的,getContentType
包括了当前的charset
。这就是你看到它与期望的Content-Type
值一起的原因。
即使你尝试使用null
值设置charset
,也不起作用,因为该方法会检测到你正在使用Writer
,并且不会被更改(参见下一个图片)
然而,有一种方法可以实现你想要的效果:
} catch (JwtAuthorizationFailedException e) {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
response.getOutputStream().print("{ \"Simple\": \"Test\" }");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
}
使用getOutputStream().print
替代getWriter().write
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论