此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.1! |
OIDC 注销
一旦最终用户能够登录到您的应用程序,考虑他们将如何注销非常重要。
一般来说,有三个用例供您考虑:
-
我只想执行本地注销
-
我想注销我的应用程序和由我的应用程序启动的 OIDC 提供商
-
我想注销我的应用程序和由 OIDC 提供商发起的 OIDC 提供商
本地注销
要执行本地注销,不需要特殊的 OIDC 配置。
Spring Security 会自动建立本地注销端点,您可以通过logout()
DSL (英语).
OpenID Connect 1.0 客户端发起的注销
OpenID Connect 会话管理 1.0 允许使用客户端在提供程序处注销最终用户。 可用的策略之一是 RP 发起的注销。
如果 OpenID Provider 同时支持 Session Management 和 Discovery,则客户端可以获取end_session_endpoint
URL
从 OpenID 提供程序的发现元数据。
为此,您可以配置ClientRegistration
使用issuer-uri
如下:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
...
provider:
okta:
issuer-uri: https://dev-1234.oktapreview.com
此外,您应该配置OidcClientInitiatedLogoutSuccessHandler
,它实施 RP 启动的注销,如下所示:
-
Java
-
Kotlin
@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2Login(withDefaults())
.logout(logout -> logout
.logoutSuccessHandler(oidcLogoutSuccessHandler())
);
return http.build();
}
private LogoutSuccessHandler oidcLogoutSuccessHandler() {
OidcClientInitiatedLogoutSuccessHandler oidcLogoutSuccessHandler =
new OidcClientInitiatedLogoutSuccessHandler(this.clientRegistrationRepository);
// Sets the location that the End-User's User Agent will be redirected to
// after the logout has been performed at the Provider
oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}");
return oidcLogoutSuccessHandler;
}
}
@Configuration
@EnableWebSecurity
class OAuth2LoginSecurityConfig {
@Autowired
private lateinit var clientRegistrationRepository: ClientRegistrationRepository
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize(anyRequest, authenticated)
}
oauth2Login { }
logout {
logoutSuccessHandler = oidcLogoutSuccessHandler()
}
}
return http.build()
}
private fun oidcLogoutSuccessHandler(): LogoutSuccessHandler {
val oidcLogoutSuccessHandler = OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository)
// Sets the location that the End-User's User Agent will be redirected to
// after the logout has been performed at the Provider
oidcLogoutSuccessHandler.setPostLogoutRedirectUri("{baseUrl}")
return oidcLogoutSuccessHandler
}
}
|
OpenID Connect 1.0 反向通道注销
OpenID Connect 会话管理 1.0 允许通过让提供商对客户端进行 API 调用,在客户端注销最终用户。 这称为 OIDC 反向通道注销。
要启用此功能,您可以在 DSL 中建立 Back-Channel Logout 终端节点,如下所示:
-
Java
-
Kotlin
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2Login(withDefaults())
.oidcLogout((logout) -> logout
.backChannel(Customizer.withDefaults())
);
return http.build();
}
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2Login { }
oidcLogout {
backChannel { }
}
}
return http.build()
}
然后,您需要一种方法来监听 Spring Security 发布的事件来删除旧的OidcSessionInformation
条目,如下所示:
-
Java
-
Kotlin
@Bean
public HttpSessionEventPublisher sessionEventPublisher() {
return new HttpSessionEventPublisher();
}
@Bean
open fun sessionEventPublisher(): HttpSessionEventPublisher {
return HttpSessionEventPublisher()
}
这将使如果HttpSession#invalidate
,则会话也会从内存中删除。
就是这样!
这将建立端点/logout/connect/back-channel/{registrationId}
OIDC 提供商可以请求使应用程序中最终用户的给定会话失效。
oidcLogout 要求oauth2Login 也被配置。 |
oidcLogout 要求调用会话 CookieJSESSIONID 以便通过 BackChannel 正确注销每个会话。 |
反向通道注销架构
考虑一个ClientRegistration
其标识符为registrationId
.
Back-Channel 注销的总体流程如下:
-
在登录时, Spring Security 将 ID 令牌、CSRF 令牌和提供者会话 ID(如果有)与其应用程序中的会话 ID 相关联
OidcSessionRegistry
实现。 -
然后,在注销时,您的 OIDC 提供商会对
/logout/connect/back-channel/registrationId
包括一个 Logout Token,该令牌指示sub
(最终用户)或sid
(Provider Session ID) 注销。 -
Spring Security 验证令牌的签名和声明。
-
如果令牌包含
sid
claim,则仅终止与该 provider 会话相关的 Client 会话。 -
否则,如果令牌包含
sub
claim,则该 Client 对该 End User 的所有会话都将终止。
请记住,Spring Security 的 OIDC 支持是多租户的。
这意味着它只会终止 Client 与aud claim 的 Token。 |
自定义 OIDC Provider Session Registry
默认情况下,Spring Security 将 OIDC Provider 会话和 Client 会话之间的所有链接存储在内存中。
在许多情况下,例如集群应用程序,最好将其存储在单独的位置(如数据库)中。
您可以通过配置自定义OidcSessionRegistry
这样:
-
Java
-
Kotlin
@Component
public final class MySpringDataOidcSessionRegistry implements OidcSessionRegistry {
private final OidcProviderSessionRepository sessions;
// ...
@Override
public void saveSessionInformation(OidcSessionInformation info) {
this.sessions.save(info);
}
@Override
public OidcSessionInformation removeSessionInformation(String clientSessionId) {
return this.sessions.removeByClientSessionId(clientSessionId);
}
@Override
public Iterable<OidcSessionInformation> removeSessionInformation(OidcLogoutToken token) {
return token.getSessionId() != null ?
this.sessions.removeBySessionIdAndIssuerAndAudience(...) :
this.sessions.removeBySubjectAndIssuerAndAudience(...);
}
}
@Component
class MySpringDataOidcSessionRegistry: OidcSessionRegistry {
val sessions: OidcProviderSessionRepository
// ...
@Override
fun saveSessionInformation(info: OidcSessionInformation) {
this.sessions.save(info)
}
@Override
fun removeSessionInformation(clientSessionId: String): OidcSessionInformation {
return this.sessions.removeByClientSessionId(clientSessionId);
}
@Override
fun removeSessionInformation(token: OidcLogoutToken): Iterable<OidcSessionInformation> {
return token.getSessionId() != null ?
this.sessions.removeBySessionIdAndIssuerAndAudience(...) :
this.sessions.removeBySubjectAndIssuerAndAudience(...);
}
}