You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Jetty服务器时,Spring Security中JWT拦截后如何返回JSON格式401错误?

问题:Jetty下Spring Security JWT过滤器返回HTML格式响应而非JSON

我的Spring Security Bean在Tomcat服务器下能正常拦截未授权请求,返回包含错误消息的简洁JSON异常响应;但切换到Jetty服务器后,即使在Postman中请求,也会返回text/html格式的响应。

现有JwtFilter代码

public class JwtFilter extends OncePerRequestFilter {

    @Autowired
    private JWTUtility jwtUtility;

    @Autowired
    private UserServiceImpl userService;
    private final ObjectMapper mapper;


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String authorization = httpServletRequest.getHeader("Authorization");
        String token = null;
        String userName = null;

        if(null != authorization && authorization.startsWith("Bearer ")) {
            token = authorization.substring(7);
            userName = jwtUtility.getUsernameFromToken(token);
        }

        if(null != userName && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails
                    = userService.loadUserByUsername(userName);
            try {
                if (jwtUtility.validateToken(token, userDetails)) {
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken
                            = new UsernamePasswordAuthenticationToken(userDetails,
                            null, userDetails.getAuthorities());

                    usernamePasswordAuthenticationToken.setDetails(
                            new WebAuthenticationDetailsSource().buildDetails(httpServletRequest)
                    );

                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
                }
            } catch (Exception e){
//                System.out.println("Hello world");
                Map<String, Object> errorDetails = new HashMap<>();
                errorDetails.put("ACCESS_DENIED", e.getMessage());
                httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
                httpServletResponse.setContentType(String.valueOf(MediaType.APPLICATION_JSON));
                mapper.writeValue(httpServletResponse.getWriter(), errorDetails);
                return;
            }

        }

        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

需要替换的代码段

Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("ACCESS_DENIED", e.getMessage());
httpServletResponse.setStatus(HttpStatus.FORBIDDEN.value());
httpServletResponse.setContentType(String.valueOf(MediaType.APPLICATION_JSON));
mapper.writeValue(httpServletResponse.getWriter(), errorDetails);
return;

期望的响应效果

当用户未发送JWT时,返回:

HTTP 401 Unauthorized
{
    "ACCESS_DENIED": "Some error message here"
}

合法请求则正常通过过滤器,无需返回额外内容。


修改方案

针对Jetty的响应处理特性,调整异常处理代码如下:

Map<String, Object> errorDetails = new HashMap<>();
errorDetails.put("ACCESS_DENIED", e.getMessage());

// 设置符合期望的未授权状态码
httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
// 明确指定JSON内容类型及UTF-8编码,避免Jetty自动转为HTML格式
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.setCharacterEncoding("UTF-8");

// 使用OutputStream写入响应,规避Jetty对Writer的兼容问题
mapper.writeValue(httpServletResponse.getOutputStream(), errorDetails);

// 强制刷新缓冲区,确保响应内容立即发送
httpServletResponse.flushBuffer();
return;

关键调整说明

  • 状态码修正:将原403(FORBIDDEN)改为401(UNAUTHORIZED),匹配未授权场景的语义。
  • 内容类型与编码:Jetty对响应头处理更严格,明确指定完整的application/json;charset=UTF-8,避免被容器默认的HTML格式覆盖。
  • OutputStream替代Writer:部分Jetty版本中,使用getWriter()可能触发响应格式自动转换,改用getOutputStream()可确保JSON内容直接写入。
  • 强制刷新缓冲区:调用flushBuffer()避免后续过滤器或容器修改响应格式,保证内容按预期发送。

另外,当前代码仅处理token验证失败的场景,若要覆盖未发送JWT的情况,需在authorization == null时添加对应逻辑:

if (null == authorization) {
    Map<String, Object> errorDetails = new HashMap<>();
    errorDetails.put("ACCESS_DENIED", "JWT token is missing");
    httpServletResponse.setStatus(HttpStatus.UNAUTHORIZED.value());
    httpServletResponse.setContentType("application/json;charset=UTF-8");
    httpServletResponse.setCharacterEncoding("UTF-8");
    mapper.writeValue(httpServletResponse.getOutputStream(), errorDetails);
    httpServletResponse.flushBuffer();
    return;
}

内容的提问来源于stack exchange,提问作者Jet Ezra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:05:33