【问题标题】:Return custom http error message from Spring Security filter从 Spring Security 过滤器返回自定义 http 错误消息
【发布时间】:2021-01-28 14:06:13
【问题描述】:

我正在构建一个 REST API,它使用 Spring Security(及其过滤器链)通过 JWT 对用户进行身份验证。现在,如果这样的 JWT 丢失、过期或类似情况,我想向 API 使用者返回格式正确的错误消息,而不是默认的白标签错误响应。从 Spring Security 过滤器返回的 API 错误消息应该与业务逻辑失败时返回的错误消息相同。

如果业务逻辑失败,我的 Spring REST 控制器会返回格式如下的错误消息(通过@RestControllerAdvice):

Content-Type: application/json

{
  "code": "VOUCHER_NOT_FOUND",
  "message": "The specified voucher code was not found.",
  "timestamp": "2020-09-06T21:22:23.015Z"
}

我知道如果 Spring Security 过滤器链中发生错误,控制器将永远无法到达,因此我必须从安全过滤器中返回一条 HTTP 错误消息。我试过这样做:

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\" }");
        }

}

问题是,我得到的错误消息总是设置不同的Content-Type 标头(添加了charset=ISO-8859-1):

Content-Type: application/json;charset=ISO-8859-1

{
  "Simple": "Test"
}

我想简化这一点并使其保持一致。所以问题是,我怎样才能确定,只有

Content-Type: application/json

是从安全过滤器返回的?我尝试了很多选择,比如

response.setHeader("Content-Type", "application/json");

response.setContentType(MediaType.APPLICATION_JSON_VALUE);

但所有这些都不起作用。有什么想法吗?

【问题讨论】:

    标签: java spring-boot rest spring-security


    【解决方案1】:

    本例的问题来自getWriter()方法:

    这个将默认字符编码包含在将返回的Response 中,正如您在下一张图片中看到的那样,它会在返回的Content-Type 中引发“附加信息” .

    当 Spring 序列化响应时,使用“getter 方法”,如您所见,getContentType 包括当前的charset。这就是您看到除了所需的Content-Type 值之外的原因。

    即使您尝试将 charset 设置为 null 值,它也不起作用,因为该方法会检测到您正在使用 Writer 并且不会更改(请参见下一张图片)

    但是,有一种方法可以实现您想要的:

    } catch (JwtAuthorizationFailedException e) {
      response.setStatus(HttpStatus.UNAUTHORIZED.value());
      response.getOutputStream().print("{ \"Simple\": \"Test\" }");
      response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    }
    

    使用getOutputStream().print 代替getWriter().write

    【讨论】:

    • 非常感谢您的解释。这很好用!
    猜你喜欢
    • 2016-03-08
    • 2019-01-29
    • 2017-05-15
    • 2016-10-06
    • 2011-08-23
    • 2014-07-28
    • 2022-11-05
    • 2013-07-22
    • 1970-01-01
    相关资源
    最近更新 更多