Keycloak SSO会话闲置超时未触发,JHipster应用配置求助
Keycloak会话闲置超时未触发登出问题排查(JHipster+Spring Boot环境)
问题描述
我们的应用基于JHipster构建,技术栈包含Spring Boot、Keycloak和PostgreSQL数据库。已在Keycloak领域设置中将「SSO Session Idle」时间配置为1分钟,预期用户闲置1分钟及以上时Keycloak会发送登出事件,但实际无任何反应:Keycloak无相关日志,会话仍处于活跃状态,用户可正常调用其他REST API。
相关日志
登录Keycloak时观察到以下日志:
03:57:20,717 WARN [org.keycloak.events] (default task-64) type=REFRESH_TOKEN_ERROR, realmId=google, clientId=youtube, userId=8299cea8-8ebf-45df-8685-b37445620255, ipAddress=10.198.140.148, error=invalid_token, grant_type=refresh_token, refresh_token_type=Offline, refresh_token_id=2b031b6d-5ff4-4967-a300-42b930dfc04b, client_auth_method=client-secret
解决方案:Spring Boot配置调整
要让JHipster应用接收Keycloak会话过期事件并实现自动登出,需在application.yaml或application_prod.yaml中添加以下关键配置:
1. 启用Keycloak会话校验
让应用定期同步Keycloak的会话状态,确保本地会话和Keycloak端保持一致:
spring: security: oauth2: resourceserver: jwt: issuer-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm} jwk-set-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/certs client: provider: keycloak: issuer-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm} registration: keycloak: client-id: ${keycloak.client-id} client-secret: ${keycloak.client-secret} authorization-grant-type: authorization_code redirect-uri: "{baseUrl}/login/oauth2/code/keycloak" scope: openid,profile,email keycloak: auth-server-url: <你的Keycloak服务地址> realm: <你的领域名称> client-id: <你的客户端ID> client-secret: <你的客户端密钥> use-resource-role-mappings: true bearer-only: false # 开启Keycloak会话状态校验 check-token-realm: true # 用JPA存储令牌,便于同步会话状态 token-store: jpa
2. 同步本地会话超时时间
设置Spring Boot本地会话超时和Keycloak的SSO闲置时间一致,避免本地会话残留:
server: servlet: session: timeout: 1m tracking-modes: cookie
3. 主动处理Keycloak登出事件(可选)
如果需要主动监听Keycloak的登出事件,可以实现自定义登出处理器,确保应用端同步登出:
@Component public class CustomKeycloakLogoutHandler implements LogoutHandler { private final OAuth2AuthorizedClientService authorizedClientService; public CustomKeycloakLogoutHandler(OAuth2AuthorizedClientService authorizedClientService) { this.authorizedClientService = authorizedClientService; } @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication; OAuth2AuthorizedClient client = authorizedClientService.loadAuthorizedClient( oauthToken.getAuthorizedClientRegistrationId(), oauthToken.getName()); if (client != null) { String logoutUrl = client.getClientRegistration() .getProviderDetails() .getConfigurationMetadata() .get("end_session_endpoint") + "?id_token_hint=" + client.getAccessToken().getTokenValue(); try { response.sendRedirect(logoutUrl); } catch (IOException e) { throw new RuntimeException("跳转Keycloak登出端点失败", e); } } } }
然后在安全配置类中注册该处理器:
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { private final CustomKeycloakLogoutHandler logoutHandler; public SecurityConfiguration(CustomKeycloakLogoutHandler logoutHandler) { this.logoutHandler = logoutHandler; } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .anyRequest().authenticated() .and() .oauth2Login() .and() .logout() .addLogoutHandler(logoutHandler) .logoutSuccessUrl("/"); } }
日志异常说明
日志中的REFRESH_TOKEN_ERROR是离线刷新令牌无效导致的,和当前会话闲置超时问题无关。如果应用使用了离线令牌,需检查Keycloak中离线令牌的有效期配置,避免影响正常令牌刷新流程。
Keycloak端额外检查
- 确认Keycloak领域的「SSO Session Idle」设置已正确保存,未被客户端级别的
Access Token Lifespan等参数覆盖 - 检查Keycloak的「Session Idle」和「Session Max」时间配置,确保闲置超时规则生效
- 确保Keycloak事件日志级别设置为
WARN或更高,以便能捕获到会话过期相关日志
内容的提问来源于stack exchange,提问作者Raj Paul
相关产品推荐
相关产品推荐

