Keycloak与Spring Security Adapter集成时会话超时异常及解决咨询
解决Keycloak SSO会话超时后Spring Security未自动登出的问题
这个场景确实是老版本Keycloak+Spring Security集成时的常见坑——当Keycloak端的SSO会话已经过期,Spring应用端的认证会话还没失效,导致用户拿着过期的访问令牌(刷新令牌也失效了)还能继续访问资源。下面结合你使用的版本(Spring Security 4.0.4、Keycloak适配器3.4.2、Keycloak服务器3.4.3),给出具体的解决方案:
核心思路
捕获Keycloak适配器抛出的RefreshTokenFailedException异常,在异常处理中清除Spring Security的认证会话,并将用户重定向到登录页。
步骤1:自定义认证失败处理器
继承Keycloak默认的认证失败处理器,专门处理刷新令牌失效的情况:
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationFailureHandler; import org.keycloak.adapters.springsecurity.exception.RefreshTokenFailedException; import org.springframework.security.core.AuthenticationException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class CustomKeycloakAuthFailureHandler extends KeycloakAuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { // 精准匹配刷新令牌失效的异常 if (exception instanceof RefreshTokenFailedException) { // 清除Spring端的会话和认证信息 request.getSession().invalidate(); // 重定向到Keycloak登录入口(根据你的应用路径调整) response.sendRedirect(request.getContextPath() + "/sso/login"); return; } // 其他类型的认证异常交给父类默认处理 super.onAuthenticationFailure(request, response, exception); } }
步骤2:配置Spring Security,替换默认的失败处理器
在你的Spring Security配置类中,将自定义的失败处理器绑定到Keycloak认证提供者,并确保异常处理逻辑生效:
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents; import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider; import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.session.SessionRegistryImpl; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; @Configuration @EnableWebSecurity @ComponentScan(basePackageClasses = KeycloakSecurityComponents.class) public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter { @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { KeycloakAuthenticationProvider keycloakAuthProvider = keycloakAuthenticationProvider(); // 绑定自定义的失败处理器 keycloakAuthProvider.setAuthenticationFailureHandler(customKeycloakAuthFailureHandler()); auth.authenticationProvider(keycloakAuthProvider); } @Bean @Override protected SessionAuthenticationStrategy sessionAuthenticationStrategy() { return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl()); } @Bean public CustomKeycloakAuthFailureHandler customKeycloakAuthFailureHandler() { return new CustomKeycloakAuthFailureHandler(); } @Override protected void configure(HttpSecurity http) throws Exception { super.configure(http); http .authorizeRequests() .anyRequest().authenticated() .and() .logout() .logoutSuccessUrl("/") .invalidateHttpSession(true); // 登出时强制清除会话 // 确保Keycloak的认证入口逻辑正常 http.exceptionHandling().authenticationEntryPoint(keycloakAuthenticationEntryPoint()); } }
额外注意事项
- 对齐Token过期时间:在Keycloak客户端配置中,确保Refresh Token的过期时间不晚于SSO会话的过期时间,这样当SSO会话超时后,Refresh Token也会同步失效,触发我们的异常处理。
- 测试验证:可以在Keycloak控制台手动强制登出用户,然后访问应用受保护的资源,验证是否会自动重定向到登录页。
- 版本适配:你的版本比较老旧,上述代码是针对Spring Security 4.x和Keycloak 3.x编写的,不要直接照搬新版本的API。
内容的提问来源于stack exchange,提问作者lazyneuron
相关产品推荐
相关产品推荐

