Spring Boot OAuth2客户端集成Spring Session xAuthToken引发无限重定向循环
问题分析与解决方案
无限重定向的核心原因是:OAuth2授权码流程中,认证成功后的重定向请求里,浏览器不会自动携带x-auth-token请求头,导致客户端无法读取到之前存储在Session中的授权状态(比如state参数),进而认为用户未完成授权,反复跳转至授权服务器。
要实现用x-auth-token替代JSESSIONID,同时解决重定向循环,需要拆分处理OAuth2认证流程的状态存储和RestController的Session识别:
1. 替换OAuth2授权请求状态存储方式
默认的HttpSessionOAuth2AuthorizationRequestRepository依赖HttpSession传递授权状态,但切换到Header式Session解析后,重定向请求无法携带Session头,因此需要改用Cookie存储授权状态。
自定义Cookie-based授权请求仓库
class CookieBasedOAuth2AuthorizationRequestRepository : OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> { private val OAUTH2_AUTH_REQUEST_COOKIE = "oauth2_auth_request" private val REDIRECT_URI_COOKIE = "redirect_uri" private val COOKIE_EXPIRE_SECONDS = 180 override fun loadAuthorizationRequest(request: HttpServletRequest): OAuth2AuthorizationRequest? { return getCookie(request, OAUTH2_AUTH_REQUEST_COOKIE) .map { ObjectMapper().readValue(it.value, OAuth2AuthorizationRequest::class.java) } .orElse(null) } override fun saveAuthorizationRequest(authRequest: OAuth2AuthorizationRequest?, request: HttpServletRequest, response: HttpServletResponse) { if (authRequest == null) { clearAuthRequestCookies(request, response) return } val cookieValue = ObjectMapper().writeValueAsString(authRequest) addCookie(response, OAUTH2_AUTH_REQUEST_COOKIE, cookieValue) request.getParameter("redirect_uri")?.let { addCookie(response, REDIRECT_URI_COOKIE, it) } } override fun removeAuthorizationRequest(request: HttpServletRequest, response: HttpServletResponse): OAuth2AuthorizationRequest? { return loadAuthorizationRequest(request).also { clearAuthRequestCookies(request, response) } } private fun clearAuthRequestCookies(request: HttpServletRequest, response: HttpServletResponse) { listOf(OAUTH2_AUTH_REQUEST_COOKIE, REDIRECT_URI_COOKIE).forEach { cookieName -> getCookie(request, cookieName)?.let { it.maxAge = 0 response.addCookie(it) } } } private fun addCookie(response: HttpServletResponse, name: String, value: String) { val cookie = Cookie(name, value) cookie.path = "/" cookie.isHttpOnly = true cookie.maxAge = COOKIE_EXPIRE_SECONDS response.addCookie(cookie) } private fun getCookie(request: HttpServletRequest, name: String): Optional<Cookie> { return Optional.ofNullable(request.cookies) ?.filter { it.name == name } ?.firstOrNull() ?.let { Optional.of(it) } ?: Optional.empty() } }
2. 配置Spring Security与Session解析器
在配置类中注册自定义授权仓库,并保留Header式Session解析器:
@Configuration class SecurityConfig { // 注册Header式Session解析器 @Bean fun sessionResolver(): HttpSessionIdResolver = HeaderHttpSessionIdResolver.xAuthToken() // 注册Cookie-based授权请求仓库 @Bean fun authorizationRequestRepository(): OAuth2AuthorizationRequestRepository<OAuth2AuthorizationRequest> { return CookieBasedOAuth2AuthorizationRequestRepository() } // 配置Security Filter Chain @Bean fun filterChain(http: HttpSecurity): SecurityFilterChain { http .authorizeHttpRequests { auth -> auth.anyRequest().authenticated() } .oauth2Login { oauth2 -> // 指定使用自定义的授权请求仓库 oauth2.authorizationRequestRepository(authorizationRequestRepository()) } .sessionManagement { session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) } return http.build() } }
3. RestController请求处理
后续调用demo-client的RestController端点时,只需在请求头中携带x-auth-token,Spring Session即可通过该头识别Session,无需依赖JSESSIONID Cookie。
内容的提问来源于stack exchange,提问作者salmas
相关产品推荐
相关产品推荐

