Keycloak+Spring Security OIDC:SSO会话超时后无法重定向至登录页
我有一个配置为Spring Security OAuth2 Client的Spring Boot应用,使用Keycloak作为OIDC提供商,后端同时提供编译后的Angular前端应用。Angular应用每秒向后端REST API发起请求。
用户在浏览器打开localhost:4881时,会被重定向到Keycloak登录页,登录成功后返回该地址渲染Angular应用。
用户登录后,Keycloak中会创建两个会话:Regular SSO和Offline。Regular SSO会话会按SSO空闲超时设置被清除,但Offline会话会在Keycloak中保留更久才被清除。
当前问题是:SSO会话触发空闲/最大超时后,UI无法自动重定向到Keycloak登录页。我认为Keycloak应该将超时事件通知OAuth2客户端,客户端需要将后续所有请求对应的UI重定向至登录页。
我已完成的配置:
- 在Realm级别设置了
SSO Session Idle、SSO Session Max和Offline Session Settings - 配置了客户端的
Admin URL
我创建了同配置的示例应用,包含:
backend:配置为OAuth2 Client的Spring Boot应用,提供/api/currentTime接口返回当前时间frontend:每秒调用后端接口获取时间的Angular应用realm:配置文件路径为keycloak-oauth2-client-ui\backend\src\main\resources\demo-realm.json- 测试账号密码:
demo/demo
1. 后端配置:处理OAuth2会话失效异常
Spring Security OAuth2 Client不会主动检测Keycloak的SSO会话超时,需配置异常处理器,在请求因令牌失效/会话过期返回401时,触发重定向或返回明确状态码。
配置Spring Security异常处理
在Spring Security配置类中添加认证异常处理逻辑:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/api/**").authenticated() .anyRequest().permitAll() ) .oauth2Login(Customizer.withDefaults()) .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults())) .exceptionHandling(ex -> ex .authenticationEntryPoint((request, response, authException) -> { // 识别OAuth2认证异常,重定向到Keycloak登录触发路径 if (authException instanceof OAuth2AuthorizationException) { response.sendRedirect("/oauth2/authorization/keycloak"); } else { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); } }) ); return http.build(); } }
2. 前端配置:捕获401状态触发重定向
Angular应用需通过HTTP拦截器捕获后端返回的401状态码,主动跳转到后端登录触发路径,由后端完成Keycloak重定向。
创建Angular HTTP拦截器
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; @Injectable() export class AuthInterceptor implements HttpInterceptor { intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { return next.handle(request).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { // 跳转到后端登录触发地址,触发Keycloak重定向 window.location.href = '/oauth2/authorization/keycloak'; } return throwError(() => error); }) ); } }
注册拦截器到AppModule
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
3. Keycloak端补充配置
- 开启客户端的
Front Channel Logout选项:当SSO会话超时或注销时,Keycloak会通过前端iframe通知客户端清除会话,触发重定向逻辑。 - 检查SSO会话Cookie配置:确保
KEYCLOAK_SESSION的HttpOnly、SameSite参数设置正确,保证前端能正确感知会话状态变化。
4. 处理Offline会话的影响
Offline会话用于长期授权,但SSO会话超时后,浏览器端的SSO会话已失效,需强制用户重新登录建立新的SSO会话,避免依赖Offline会话绕过登录流程。
内容的提问来源于stack exchange,提问作者Saketh

