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

为何Spring中抛出ResponseStatusException(400)却返回403状态码?

问题:抛出ResponseStatusException后返回403而非设置的400状态码

我有一个Spring Boot应用,安全配置如下:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .csrf().disable()
        .headers().frameOptions().sameOrigin().and()
        .addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class)
        .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
        .authorizeRequests()
            .antMatchers("/api/authenticate/**", "/h2-console/**").permitAll()
            .anyRequest().authenticated();
}

在业务代码中,我抛出了ResponseStatusException:

if (existingTenant.isPresent()) {
    throw new ResponseStatusException(
        HttpStatusCode.valueOf(400),
        "Tenant with name " + tenant.name() + " already exists");
}

但API实际返回的是403状态码:

* Mark bundle as not supporting multiuse
< HTTP/1.1 403 
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: SAMEORIGIN
< Content-Length: 0
< Date: Sat, 13 Aug 2022 19:26:27 GMT
< 
* Connection #0 to host localhost left intact

仅看到如下日志:

2022-08-13T12:32:09.260-07:00 WARN 79360 --- [nio-8080-exec-6] .w.s.m.a.ResponseStatusExceptionResolver : Resolved [org.springframework.web.server.ResponseStatusException: 400 BAD_REQUEST "Tenant with name tenant1 already exists"]

为何响应没有返回我在异常中设置的400状态码?


解答

从日志能看到ResponseStatusExceptionResolver已经成功把异常解析为400状态码,但实际返回403,说明异常解析完成后,有后续组件(大概率是你配置的JWT过滤器jwtTokenFilter)修改了响应状态码,或者过滤器的异常处理逻辑导致Spring Security返回了403。

排查方向及解决方案:

  1. 检查JWT过滤器的实现

    • 查看jwtTokenFilter的doFilter方法,是否在调用chain.doFilter(request, response)后强制覆盖了响应状态码;或者捕获异常时没有重新抛出,而是自行设置了403状态码。
    • 正确的过滤器实现要保证异常能被后续异常解析器处理,示例如下:
      @Override
      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
          try {
              // 执行JWT校验逻辑
              chain.doFilter(request, response); // 必须调用此方法,让请求继续流转
          } catch (Exception e) {
              // 不要自行设置状态码,直接抛出异常交给Spring的异常处理器处理
              throw e;
          }
      }
      
  2. 确认请求是否通过认证

    • 虽然日志显示异常被解析,但如果JWT过滤器在业务代码抛出异常前就判定请求未通过认证,Spring Security会直接返回403,此时业务代码的异常只是被记录,实际响应已被Security覆盖。可以在过滤器中添加日志,确认认证是否成功。
  3. 检查Spring Security异常处理器配置

    • 若自定义了AccessDeniedHandler或AuthenticationEntryPoint,确认这些处理器没有错误地将所有异常统一处理为403状态码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 10:33:47