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

Spring Boot+Keycloak:特定客户端会话过期后强制SSO重新验证登录

解决方案

针对你遇到的应用会话过期后Keycloak自动登录的问题,以下是几个可行的解决办法:

1. 强制OAuth2认证时带上prompt=login参数

通过修改Spring Security的OAuth2登录配置,在认证请求中添加prompt=login参数,这样不管Keycloak端是否存在有效会话,都会强制用户重新输入凭证验证身份。

修改你的OAuth2LoginSecurityConfig,添加自定义的OAuth2AuthorizationRequestResolver:

import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class OAuth2LoginSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        // 自定义授权请求解析器,添加prompt=login参数
        OAuth2AuthorizationRequestResolver authorizationRequestResolver =
                new DefaultOAuth2AuthorizationRequestResolver(
                        clientRegistrationRepository, "/oauth2/authorization") {
                    @Override
                    protected OAuth2AuthorizationRequest customizeAuthorizationRequest(
                            OAuth2AuthorizationRequest authorizationRequest, HttpServletRequest request) {
                        Map<String, Object> additionalParameters = new HashMap<>(authorizationRequest.getAdditionalParameters());
                        additionalParameters.put("prompt", "login");
                        return OAuth2AuthorizationRequest.from(authorizationRequest)
                                .additionalParameters(additionalParameters)
                                .build();
                    }
                };

        http.authorizeRequests()
                .antMatchers("/actuator/**").permitAll()
                .anyRequest().hasRole("special")
                .and()
                .oauth2Login(oauth2 -> oauth2
                        .authorizationEndpoint(endpoint -> endpoint
                                .authorizationRequestResolver(authorizationRequestResolver)));
        return http.build();
    }
}

该配置会让每次触发OAuth2登录流程时,强制Keycloak要求用户重新登录。

2. 注销应用会话时同步注销Keycloak SSO会话

如果希望用户主动注销应用后,同时清除Keycloak端的SSO会话以避免自动登录,可以自定义LogoutHandler,调用Keycloak的注销端点。

首先,添加Keycloak Spring Boot Starter依赖(匹配你的Keycloak版本):

<dependency>
    <groupId>org.keycloak</groupId>
    <artifactId>keycloak-spring-boot-starter</artifactId>
    <version>22.0.5</version>
</dependency>

实现自定义LogoutHandler:

import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class KeycloakLogoutHandler implements LogoutHandler {

    private final Keycloak keycloakAdminClient;
    private final String realm = "master";

    public KeycloakLogoutHandler() {
        // 初始化Keycloak Admin客户端,建议通过配置文件注入参数,避免硬编码
        this.keycloakAdminClient = KeycloakBuilder.builder()
                .serverUrl("https://localhost")
                .realm(realm)
                .clientId("admin-cli")
                .username("admin") // 替换为你的Keycloak管理员账号
                .password("admin-password") // 替换为管理员密码
                .build();
    }

    @Override
    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
        if (authentication instanceof KeycloakAuthenticationToken token) {
            String userId = token.getAccount().getKeycloakSecurityContext().getToken().getSubject();
            // 注销用户的SSO会话
            keycloakAdminClient.realm(realm).users().get(userId).logout();
        }
    }
}

在SecurityFilterChain中配置注销逻辑:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http, ClientRegistrationRepository clientRegistrationRepository) throws Exception {
    // ... 保留原有授权配置
    
    http.logout(logout -> logout
            .addLogoutHandler(new KeycloakLogoutHandler())
            .logoutSuccessUrl("/"));
            
    return http.build();
}

3. 在Keycloak客户端配置中强制重新认证

直接在Keycloak控制台修改客户端配置,全局强制该客户端的用户重新认证:

  • 进入Keycloak控制台,选择master realm,找到你的my-Client客户端
  • 切换到Login Settings标签页
  • 勾选Require Reauthentication选项并保存

该配置会让Keycloak在每次客户端请求认证时,都要求用户重新验证,即使存在有效的SSO会话,对所有使用该客户端的应用生效。

4. 调整应用端会话超时与认证状态检查

配置应用会话超时,同时通过拦截器检查会话有效性,过期则引导用户重新登录:

在application.yml中添加会话超时配置:

server:
  servlet:
    session:
      timeout: 30m # 设置应用会话超时时间,例如30分钟

添加会话验证拦截器:

import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SessionValidationInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        // 应用会话不存在且非登录/健康检查路径时,重定向到登录页
        if (session == null && !request.getRequestURI().startsWith("/oauth2/authorization") 
            && !request.getRequestURI().startsWith("/actuator")) {
            response.sendRedirect("/oauth2/authorization/master");
            return false;
        }
        return true;
    }
}

注册拦截器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new SessionValidationInterceptor())
                .excludePathPatterns("/actuator/**", "/oauth2/**");
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 10:52:49