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

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());
    }
}

额外注意事项

  1. 对齐Token过期时间:在Keycloak客户端配置中,确保Refresh Token的过期时间不晚于SSO会话的过期时间,这样当SSO会话超时后,Refresh Token也会同步失效,触发我们的异常处理。
  2. 测试验证:可以在Keycloak控制台手动强制登出用户,然后访问应用受保护的资源,验证是否会自动重定向到登录页。
  3. 版本适配:你的版本比较老旧,上述代码是针对Spring Security 4.x和Keycloak 3.x编写的,不要直接照搬新版本的API。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:27:52