# 执行单注销
Spring 安全性支持RP和AP发起的SAML2.0单注销。
简单地说,有两个用例 Spring 安全支持:
**RP-initiated **-你的应用程序有一个端点,当发送到该端点时,该端点将注销该用户并向断言的一方发送
saml2:LogoutRequest
。在此之后,断言方会返回一个saml2:LogoutResponse
并允许你的应用程序进行响应**AP-initied **-你的应用程序有一个端点,它将从主张方接收
saml2:LogoutRequest
。你的应用程序将在此时完成注销,然后向断言的一方发送saml2:LogoutResponse
。
在**AP-initied **场景中,应用程序在注销后进行的任何本地重定向都是没有意义的。 一旦应用程序发送了 saml2:LogoutResponse ,它就不再具有对浏览器的控制权。 |
---|
# 单注销的最小配置
要使用 Spring Security的SAML2.0单注销功能,你将需要以下功能:
首先,断言一方必须支持SAML2.0单注销
其次,应该将断言一方配置为签名并发布
saml2:LogoutRequest
s和saml2:LogoutResponse
s你的应用程序的/logout/saml2/slo
端点第三,你的应用程序必须具有用于签名
saml2:LogoutRequest
s和saml2:LogoutResponse
s的PKCS#8私钥和X.509证书
你可以从最初的最小示例开始,然后添加以下配置:
@Value("${private.key}") RSAPrivateKey key;
@Value("${public.certificate}") X509Certificate certificate;
@Bean
RelyingPartyRegistrationRepository registrations() {
Saml2X509Credential credential = Saml2X509Credential.signing(key, certificate);
RelyingPartyRegistration registration = RelyingPartyRegistrations
.fromMetadataLocation("https://ap.example.org/metadata")
.registrationId("id")
.singleLogoutServiceLocation("{baseUrl}/logout/saml2/slo")
.signingX509Credentials((signing) -> signing.add(credential)) (1)
.build();
return new InMemoryRelyingPartyRegistrationRepository(registration);
}
@Bean
SecurityFilterChain web(HttpSecurity http, RelyingPartyRegistrationRepository registrations) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.saml2Login(withDefaults())
.saml2Logout(withDefaults()); (2)
return http.build();
}
1 | -首先,将你的签名键添加到RelyingPartyRegistration 实例或多个实例实例 |
---|---|
2 | -其次,指示你的应用程序想要使用SAML SLO注销最终用户 |
# 运行时期望
给定上述配置,任何登录的用户都可以向你的应用程序发送POST /logout
,以执行RP发起的SLO。然后,你的应用程序将执行以下操作:
注销用户并使其无效会话
使用
Saml2LogoutRequestResolver
来创建、签名和序列化基于与当前登录用户关联的[RelyingPartyRegistration
](login/overview.html# Servlet-saml2login-relyingpartyregistration)的<saml2:LogoutRequest>
。根据[
RelyingPartyRegistration
](login/overview.html# Servlet-saml2login-relyingpartyregistration)向断言的一方发送重定向或帖子反序列化、验证和处理由断言方发送的
<saml2:LogoutResponse>
重定向到任何配置成功的注销端点
此外,当断言一方向<saml2:LogoutRequest>
发送/logout/saml2/slo
时,你的应用程序可以参与AP发起的注销:
使用
Saml2LogoutRequestHandler
来反序列化、验证和处理由主张方发送的<saml2:LogoutRequest>
注销用户并使其无效会话
基于与刚刚注销的用户关联的[
RelyingPartyRegistration
](login/overview.html# Servlet-saml2login-relyingpartyregistration),创建、签名和序列化<saml2:LogoutResponse>
基于[
RelyingPartyRegistration
](login/overview.html# Servlet-saml2login-relyingpartyregistration)向断言方发一个重定向或发布
添加saml2Logout 会将注销的功能添加到服务提供程序。由于它是一种可选功能,因此你需要为每个单独的 RelyingPartyRegistration 启用它。你可以通过设置 RelyingPartyRegistration.Builder#singleLogoutServiceLocation 属性来实现这一点。 |
---|
# 配置注销端点
不同的端点可以触发三种行为:
RP-initiated logout,它允许经过身份验证的用户
POST
,并通过向断言的一方发送<saml2:LogoutRequest>
来触发注销过程。AP发起的注销,它允许断言的一方向应用程序发送
<saml2:LogoutRequest>
AP注销响应,它允许断言一方发送
<saml2:LogoutResponse>
作为对RP发起的<saml2:LogoutRequest>
的响应
第一种是当主体类型Saml2AuthenticatedPrincipal
时,执行正常的POST /logout
触发。
第二种是通过发送到带有由主张方签名的SAMLRequest
的/logout/saml2/slo
端点来触发的。
第三种是通过发送到带有由主张方签名的SAMLResponse
的/logout/saml2/slo
端点来触发的。
因为用户已经登录或者原始注销请求是已知的,所以registrationId
已经是已知的。由于这个原因,{registrationId}
默认情况下不是这些URL的一部分。
此URL可在DSL中自定义。
例如,如果你正在将你现有的依赖方迁移到 Spring Security,那么你的断言方可能已经指向GET /SLOService.saml2
。为了减少对断言一方的配置更改,你可以在DSL中配置过滤器,如下所示:
Java
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
.logoutResponse((response) -> response.logoutUrl("/SLOService.saml2"))
);
你还应该在RelyingPartyRegistration
中配置这些端点。
# 自定义<saml2:LogoutRequest>
分辨率
通常需要在<saml2:LogoutRequest>
中设置其他值,而不是 Spring Security提供的默认值。
默认情况下, Spring Security将发出<saml2:LogoutRequest>
并提供:
Destination
属性-来自RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceLocation
ID
属性-guid<Issuer>
元素-来自RelyingPartyRegistration#getEntityId
<NameID>
元素-来自Authentication#getName
要添加其他值,可以使用委托,例如:
@Bean
Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationResolver registrationResolver) {
OpenSaml4LogoutRequestResolver logoutRequestResolver
new OpenSaml4LogoutRequestResolver(registrationResolver);
logoutRequestResolver.setParametersConsumer((parameters) -> {
String name = ((Saml2AuthenticatedPrincipal) parameters.getAuthentication().getPrincipal()).getFirstAttribute("CustomAttribute");
String format = "urn:oasis:names:tc:SAML:2.0:nameid-format:transient";
LogoutRequest logoutRequest = parameters.getLogoutRequest();
NameID nameId = logoutRequest.getNameID();
nameId.setValue(name);
nameId.setFormat(format);
});
return logoutRequestResolver;
}
然后,你可以在DSL中提供你的自定义Saml2LogoutRequestResolver
,如下所示:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
)
);
# 自定义<saml2:LogoutResponse>
分辨率
通常需要在<saml2:LogoutResponse>
中设置其他值,而不是 Spring Security提供的默认值。
默认情况下, Spring Security将发出<saml2:LogoutResponse>
并提供:
Destination
属性-来自RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceResponseLocation
ID
属性-guid<Issuer>
元素-来自RelyingPartyRegistration#getEntityId
<Status>
元素-SUCCESS
要添加其他值,可以使用委托,例如:
@Bean
public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationResolver registrationResolver) {
OpenSaml4LogoutResponseResolver logoutRequestResolver =
new OpenSaml3LogoutResponseResolver(relyingPartyRegistrationResolver);
logoutRequestResolver.setParametersConsumer((parameters) -> {
if (checkOtherPrevailingConditions(parameters.getRequest())) {
parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT);
}
});
return logoutRequestResolver;
}
然后,你可以在DSL中提供你的自定义Saml2LogoutResponseResolver
,如下所示:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
)
);
# 自定义<saml2:LogoutRequest>
身份验证
要定制验证,你可以实现自己的Saml2LogoutRequestValidator
。在这一点上,验证是最小的,因此你可以首先委托给默认的Saml2LogoutRequestValidator
,就像这样:
@Component
public class MyOpenSamlLogoutRequestValidator implements Saml2LogoutRequestValidator {
private final Saml2LogoutRequestValidator delegate = new OpenSamlLogoutRequestValidator();
@Override
public Saml2LogoutRequestValidator logout(Saml2LogoutRequestValidatorParameters parameters) {
// verify signature, issuer, destination, and principal name
Saml2LogoutValidatorResult result = delegate.authenticate(authentication);
LogoutRequest logoutRequest = // ... parse using OpenSAML
// perform custom validation
}
}
然后,你可以在DSL中提供你的自定义Saml2LogoutRequestValidator
,如下所示:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestAuthenticator(myOpenSamlLogoutRequestAuthenticator)
)
);
# 自定义<saml2:LogoutResponse>
身份验证
要定制验证,你可以实现自己的Saml2LogoutResponseValidator
。在这一点上,验证是最小的,因此你可以首先委托给默认的Saml2LogoutResponseValidator
,就像这样:
@Component
public class MyOpenSamlLogoutResponseValidator implements Saml2LogoutResponseValidator {
private final Saml2LogoutResponseValidator delegate = new OpenSamlLogoutResponseValidator();
@Override
public Saml2LogoutValidatorResult logout(Saml2LogoutResponseValidatorParameters parameters) {
// verify signature, issuer, destination, and status
Saml2LogoutValidatorResult result = delegate.authenticate(parameters);
LogoutResponse logoutResponse = // ... parse using OpenSAML
// perform custom validation
}
}
然后,你可以在DSL中提供你的自定义Saml2LogoutResponseValidator
,如下所示:
http
.saml2Logout((saml2) -> saml2
.logoutResponse((response) -> response
.logoutResponseAuthenticator(myOpenSamlLogoutResponseAuthenticator)
)
);
# 自定义<saml2:LogoutRequest>
存储
当应用程序发送<saml2:LogoutRequest>
时,该值存储在会话中,以便RelayState
参数和InResponseTo
属性中的<saml2:LogoutResponse>
可以被验证。
如果你希望将注销请求存储在会话以外的某个地方,那么可以在DSL中提供你的自定义实现,如下所示:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestRepository(myCustomLogoutRequestRepository)
)
);