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

Spring Security 6迁移问题:JSESSIONID Cookie认证失败返回401

Spring Boot 2.7 升级到 3.0(Spring Security 6.x)的认证问题排查

问题描述

将Spring Boot从2.7版本升级到3.0版本,对应Spring Security升级至6.x版本。修改了SecurityFilterChain配置,并将@EnableGlobalMethodSecurity替换为@EnableMethodSecurity后出现以下问题:

  • 携带Authorization请求头的请求可正常通过认证,且服务端会设置JSESSIONID Cookie
  • 仅携带JSESSIONID Cookie而不携带Authorization头的请求返回401未授权,日志显示请求被识别为匿名用户

旧配置(Spring Boot 2.7 + Spring Security 5.x)

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  http
   .csrf().disable()
   .cors().and()
   .authorizeRequests().anyRequest().authenticated().and()
   .httpBasic().authenticationEntryPoint(authenticationEntryPoint()).and()
   .logout().logoutSuccessHandler(logoutSuccessHandler());
  return http.build();
}

private AuthenticationEntryPoint authenticationEntryPoint() {
  return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
}

private LogoutSuccessHandler logoutSuccessHandler() {
  return new HttpStatusReturningLogoutSuccessHandler(HttpStatus.OK);
}

新配置(Spring Boot 3.0 + Spring Security 6.x)

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
  http
      .csrf(AbstractHttpConfigurer::disable)
      .cors(withDefaults())
      .authorizeHttpRequests(authorizeHttpRequestsConfigurer -> authorizeHttpRequestsConfigurer./*requestMatchers(GET, SWAGGER_WHITELIST).permitAll().*/anyRequest().authenticated())
      .httpBasic(httpBasicConfigurer -> httpBasicConfigurer.authenticationEntryPoint(authenticationEntryPoint()))
      .logout(logoutConfigurer -> logoutConfigurer.logoutSuccessHandler(logoutSuccessHandler()));
  return http.build();
}

注解修改

原注解

@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)

修改后注解

@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true)

日志信息

正常请求日志(携带Authorization头)

18:36:00.823 [http-nio-8080-exec-6] DEBUG o.s.security.web.FilterChainProxy - Securing GET /users/current
18:36:00.924 [http-nio-8080-exec-6] DEBUG o.s.s.w.a.w.BasicAuthenticationFilter - Set SecurityContextHolder to UsernamePasswordAuthenticationToken [Principal=com.example.UserDetailsAdapter@e98a174, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=null], Granted Authorities=[ROLE_ADMIN]]
18:36:00.925 [http-nio-8080-exec-6] DEBUG o.s.security.web.FilterChainProxy - Secured GET /users/current

异常请求日志(仅携带JSESSIONID Cookie)

18:36:00.948 [http-nio-8080-exec-7] DEBUG o.s.security.web.FilterChainProxy - Securing GET /users/current
18:36:00.949 [http-nio-8080-exec-7] DEBUG o.s.s.w.a.AnonymousAuthenticationFilter - Set SecurityContextHolder to anonymous SecurityContext

问题原因与解决方案

核心原因

Spring Security 6.x默认关闭了HttpSession的安全上下文持久化功能,而Spring Security 5.x(对应Spring Boot 2.7)中默认是开启的。

在Spring Security 5.x中,用户通过HttpBasic认证成功后,认证信息会自动保存到HttpSession中,后续请求携带JSESSIONID Cookie时,系统会从Session中读取认证信息完成授权。但Spring Security 6.x调整了默认行为:SecurityContextHolderFilter默认不再从HttpSession加载安全上下文,也不会将认证后的上下文保存到Session中,导致仅携带Cookie的请求无法获取已认证的用户信息,被识别为匿名用户。

解决方案

在SecurityFilterChain配置中显式开启安全上下文的Session持久化:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
        .csrf(AbstractHttpConfigurer::disable)
        .cors(withDefaults())
        .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
        // 开启安全上下文的Session持久化
        .securityContext(securityContext -> securityContext
            .securityContextRepository(new HttpSessionSecurityContextRepository())
            .requireExplicitSave(false) // 自动将认证后的上下文保存到Session
        )
        .httpBasic(basic -> basic.authenticationEntryPoint(authenticationEntryPoint()))
        .logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler()));
    return http.build();
}

验证说明

添加配置后:

  1. 用户首次携带Authorization头认证成功时,HttpSessionSecurityContextRepository会将认证后的SecurityContext保存到HttpSession,同时返回JSESSIONID Cookie
  2. 后续请求携带JSESSIONID Cookie时,SecurityContextHolderFilter会从Session中加载已认证的上下文,无需再次携带Authorization头即可完成授权

同时你会发现正常请求的日志中,SessionId不再是null,而是对应的JSESSIONID值。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 19:10:37