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

Spring Security 6.2未返回JSESSIONID及会话认证失效问题求助

解决方案:Spring Boot Kotlin 会话认证后403问题

问题根源

你手动通过AuthenticationManager完成了认证,但没有将认证成功的Authentication对象存入SecurityContext并持久化到会话。Spring Security默认的SecurityContextRepository只会自动处理过滤器链内的认证(比如表单登录、OAuth2等),自定义登录端点需要手动完成这一步。

修复步骤

1. 修改LoginController,添加会话持久化逻辑

注入SecurityContextRepository,在认证成功后将认证信息存入上下文并持久化到会话:

@RestController
class LoginController(
    private val authenticationManager: AuthenticationManager,
    private val securityContextRepository: SecurityContextRepository // 新增注入
) {

    data class LoginRequest(val username: String, val password: String)

    @PostMapping("/auth/login")
    fun login(
        @RequestBody loginRequest: LoginRequest,
        request: HttpServletRequest,
        response: HttpServletResponse
    ): ResponseEntity<Void> {
        val authenticationRequest = UsernamePasswordAuthenticationToken.unauthenticated(
            loginRequest.username, loginRequest.password
        )
        val authenticationResponse = authenticationManager.authenticate(authenticationRequest)

        // 将认证信息存入SecurityContext
        val securityContext = SecurityContextHolder.createEmptyContext()
        securityContext.authentication = authenticationResponse
        // 通过SecurityContextRepository持久化到会话
        securityContextRepository.saveContext(securityContext, request, response)

        return ResponseEntity.ok().build()
    }
}

2. 调整SecurityConfig的会话与CORS配置

  • 移除单独的WebMvcConfigurer CORS配置,改用Spring Security内置的CORS配置,避免冲突
  • 会话管理使用默认的IF_REQUIRED(仅在需要时创建会话,无需强制提前创建)

修改后的SecurityConfig:

@Configuration
class SecurityConfig {

    @Autowired
    lateinit var dataSource: DataSource

    @Value("\${cors.originPatterns:default}")
    private val corsOriginPatterns: String = ""

    @Bean
    fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            // 整合CORS配置到Spring Security
            cors {
                configurationSource { request ->
                    val corsConfig = CorsConfiguration()
                    corsConfig.allowedOriginPatterns = listOf("http://localhost:*")
                    corsConfig.allowedMethods = CorsConfiguration.ALL
                    corsConfig.allowCredentials = true
                    corsConfig
                }
            }

            authorizeHttpRequests {
                authorize("/auth/*", permitAll)
                authorize(anyRequest, authenticated)
            }

            sessionManagement {
                sessionCreationPolicy = SessionCreationPolicy.IF_REQUIRED
            }

            csrf { disable() }
        }
        return http.build()
    }

    @Bean
    fun authenticationManager(
        userDetailsService: UserDetailsService,
        passwordEncoder: PasswordEncoder
    ): AuthenticationManager {
        val authenticationProvider = DaoAuthenticationProvider()
        authenticationProvider.setUserDetailsService(userDetailsService)
        authenticationProvider.setPasswordEncoder(passwordEncoder)
        return ProviderManager(authenticationProvider)
    }

    @Bean
    fun passwordEncoder(): PasswordEncoder {
        return BCryptPasswordEncoder()
    }

    @Bean
    fun users(dataSource: DataSource): UserDetailsManager {
        return JdbcUserDetailsManager(dataSource)
    }
}

为什么之前的配置无效?

从你的调试日志可以看到,后续请求进入时AnonymousAuthenticationFilter设置了匿名上下文,说明会话中没有保存认证信息:

2024-01-08T18:53:39.470+01:00 DEBUG ... o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext

这是因为你手动认证后,没有将authenticationResponse存入SecurityContext并通过SecurityContextRepository持久化到会话,导致会话是空的,后续请求无法识别已认证用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 16:56:04