Spring Authorization Server登出时会话未删除的问题排查
问题诊断与修复方案
核心问题分析
- 登出端点配置错位:
/connect/logout是Spring Authorization Server的OIDC标准登出端点,当前被优先级更高的authorizationSecurityFilterChain拦截,但该过滤器链未配置登出逻辑,导致请求未触发会话清理与令牌失效操作。 - 自包含JWT的无状态特性:你配置了
accessTokenFormat=SELF_CONTAINED,这类JWT是无状态的,资源服务器仅校验签名和过期时间,不会主动向授权服务器校验令牌是否已被撤销,因此即使登出,未过期的JWT仍能访问资源。 - OAuth2授权记录未清理:登出时未删除数据库中存储的
OAuth2Authorization记录,导致令牌关联的授权状态仍有效。
具体修复步骤
1. 将登出配置迁移到授权服务器过滤器链
修改authorizationSecurityFilterChain,添加登出逻辑,确保/connect/logout请求被正确处理:
@Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationSecurityFilterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizationService authorizationService, SessionRegistry sessionRegistry) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) .oidc(Customizer.withDefaults()); http .sessionManagement(sessionManagement -> sessionManagement .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .maximumSessions(1) .sessionRegistry(sessionRegistry) ) .exceptionHandling((exceptions) -> exceptions .defaultAuthenticationEntryPointFor( new LoginUrlAuthenticationEntryPoint("/oauth2/authorization/keycloak"), new MediaTypeRequestMatcher(MediaType.TEXT_HTML) ) ) .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults())) // 新增登出配置 .logout(logoutConfigurer -> logoutConfigurer .logoutUrl("/connect/logout") .clearAuthentication(true) .invalidateHttpSession(true) .deleteCookies("JSESSIONID") .logoutSuccessHandler(oidcLogoutSuccessHandler(clientRegistrationRepository)) // 添加授权记录清理Handler .addLogoutHandler(new OAuth2AuthorizationLogoutHandler(authorizationService)) // 添加会话注册表清理Handler .addLogoutHandler((request, response, auth) -> { String sessionId = request.getSession(false)?.getId(); if (sessionId != null) { sessionRegistry.removeSessionInformation(sessionId); } }) ); return http.build(); }
同时删除defaultSecurityFilterChain中的登出配置,避免冲突。
2. 解决自包含JWT无法主动撤销的问题
有两种方案可选:
方案A:切换为引用令牌(推荐)
修改RegisteredClient的tokenSettings,将令牌格式改为引用类型,资源服务器会主动向授权服务器校验令牌有效性:
.tokenSettings(TokenSettings.builder() .accessTokenTimeToLive(Duration.ofMinutes(15)) // 缩短有效期提升安全性 .refreshTokenTimeToLive(Duration.ofDays(8)) .reuseRefreshTokens(false) .accessTokenFormat(OAuth2TokenFormat.REFERENCE) // 改为引用令牌 .x509CertificateBoundAccessTokens(true) .build())
方案B:配置资源服务器使用令牌 introspection(兼容自包含JWT)
在资源服务器的安全配置中,添加令牌校验端点配置:
@Bean public SecurityFilterChain resourceServerFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(authorize -> authorize .anyRequest().authenticated() ) .oauth2ResourceServer(oauth2 -> oauth2 .opaqueToken(opaqueToken -> opaqueToken .introspectionUri("http://localhost:8080/oauth2/introspect") .introspectionClientCredentials("example-client", "password") ) ); return http.build(); }
3. 前端登出流程修正
前端需遵循OIDC标准登出流程,携带id_token_hint参数调用登出端点,确保授权服务器识别当前用户:
// 前端登出示例代码 const logout = async () => { const idToken = localStorage.getItem('id_token'); const logoutUrl = `http://localhost:8080/connect/logout?id_token_hint=${idToken}&post_logout_redirect_uri=http://localhost:3000/login`; window.location.href = logoutUrl; };
验证方法
- 登出后检查数据库
oauth2_authorization表,确认对应用户的授权记录已被删除。 - 使用旧的access token调用资源服务器接口,应返回
401 Unauthorized或403 Forbidden。 - 检查浏览器
JSESSIONIDcookie已被清除,前端自动跳转到登录页。
内容的提问来源于stack exchange,提问作者Reham.Dev
相关产品推荐
相关产品推荐

