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

Spring Boot大请求体下Basic Auth返回Connection: close是否为预期行为?

Spring Boot 2.7.3大请求体触发Basic Auth返回Connection: close的问题解答

这是预期行为,根源在于Servlet容器(如Tomcat)与Spring Security的交互逻辑:

原因分析

  1. 大请求体场景:当请求体体积较大时,客户端会发送Expect: 100-continue请求头,服务器返回100 Continue后客户端开始传输请求体。此时Spring Security的Basic Auth认证逻辑触发,发现请求无凭证后返回401 Unauthorized。由于服务器已经接收了部分请求体,为防止资源泄漏,Servlet容器会主动关闭连接并返回Connection: close头——因为该连接已被部分传输的请求体污染,无法复用处理后续的认证重试请求。
  2. 小请求体场景:请求体较小时,客户端直接发送完整请求,Spring Security在服务器接收完请求体前就完成了认证检查并返回401,此时连接状态未被污染,因此可以复用,不会返回Connection: close。

解决方案

方案1:客户端启用预认证

让客户端在第一次请求时就携带Authorization: Basic <base64编码的用户名密码>头,跳过401重试流程。例如使用curl时添加-u username:password参数:

curl -u username:password -H "Accept: application/json" -v -d "@<path-to-file>\big_body.json" http://127.0.0.1:8080/demo/updateSapStatus

方案2:调整服务端配置(不推荐)

可以修改Tomcat的maxSwallowSize参数,允许服务器吞下已接收的请求体后复用连接,但这可能导致服务器资源被恶意请求消耗,存在安全风险。添加配置如下:

server.tomcat.max-swallow-size=-1

(注:设为-1表示无限制,生产环境需谨慎使用)


原问题中的请求示例

大请求体请求(返回Connection: close)

#>curl -H "Accept: application/json" -v -d "@<path-to-file>\big_body.json" http://127.0.0.1:8080/demo/updateSapStatus
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /demo/updateSapStatus HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.83.1
> Accept: application/json
> Content-Length: 1585745
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 100
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< WWW-Authenticate: Basic realm="Realm"
< 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: Thu, 17 Nov 2022 08:01:18 GMT
< Connection: close
<
* we are done reading and this is set to close, stop send
* Closing connection 0

小请求体请求(未返回Connection: close)

#>curl -H "Accept: application/json" -v -d "[]" http://127.0.0.1:8080/demo/updateSapStatus
*   Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> POST /demo/updateSapStatus HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.83.1
> Accept: application/json
> Content-Length: 2
> Content-Type: application/x-www-form-urlencoded
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 401
< WWW-Authenticate: Basic realm="Realm"
< 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: Thu, 17 Nov 2022 08:01:22 GMT
<
* Connection #0 to host 127.0.0.1 left intact

控制器代码

@RestController
@RequestMapping("/demo")
public class DemoController {

    @PostMapping(value = "/updateSapStatus")
    public ResponseEntity<String> updateSapStatus(@RequestBody List<ChangeSapStatusRequestBody> requestBody) {
        return ResponseEntity.of(Optional.of("Hello"));
    }
}

Basic Auth配置

@EnableWebSecurity
public class BasicAuthSecurityConfiguration {

    private static final String USER = "USER";


    @Bean
    public AuthenticationProvider basicAuthenticationProvider() {
        PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
        UserDetails user = User.builder()
            .username("username")
            .password(encoder.encode("password"))
            .roles(USER)
            .build();
        InMemoryUserDetailsManager userManager = new InMemoryUserDetailsManager(user);
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userManager);
        return provider;
    }

    @Bean
    @Order(1)
    public SecurityFilterChain basicAuthFilterChain(HttpSecurity http) throws Exception {
        return http
            .requestMatchers(requestMatchers -> requestMatchers.anyRequest())
            .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().hasRole(USER))
            .csrf(CsrfConfigurer::disable)
            .headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin))
            .httpBasic(withDefaults())
            .build();
    }

}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 21:35:35