|
此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.3! |
执行单点注销
Spring Security 附带对 RP 和 AP 启动的 SAML 2.0 单点注销的支持。
简而言之,Spring Security 支持两种用例:
-
RP 启动的 - 您的应用程序有一个终端节点,当 POST 到该终端节点时,将注销用户并发送一个
saml2:LogoutRequest对主张方。 此后,置位方将发回saml2:LogoutResponse并允许应用程序响应 -
AP 启动的 - 您的应用程序有一个终端节点,该终端节点将接收
saml2:LogoutRequest来自主张方。 您的应用程序将在此时完成注销,然后发送一个saml2:LogoutResponse对主张方。
在 AP 发起的场景中,您的应用程序在注销后执行的任何本地重定向都将变得毫无意义。
应用程序发送saml2:LogoutResponse,则它不再具有对浏览器的控制权。 |
单点注销的最小配置
要使用 Spring Security 的 SAML 2.0 单点注销功能,您需要满足以下条件:
-
首先,断言方必须支持 SAML 2.0 单点注销
-
其次,应将断言方配置为签名和 POST
saml2:LogoutRequests 和saml2:LogoutResponse是应用程序的/logout/saml2/slo端点 -
第三,您的应用程序必须具有 PKCS#8 私钥和 X.509 证书进行签名
saml2:LogoutRequests 和saml2:LogoutResponses
您可以从初始最小示例开始,然后添加以下配置:
@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要创建、签署和序列化<saml2:LogoutRequest>基于RelyingPartyRegistration与当前登录的用户关联。 -
反序列化、验证和处理
<saml2:LogoutResponse>由断言方发送 -
重定向到任何已配置的成功注销端点
此外,当置言方发送<saml2:LogoutRequest>自/logout/saml2/slo:
-
使用
Saml2LogoutRequestHandler反序列化、验证和处理<saml2:LogoutRequest>由断言方发送 -
注销用户并使会话失效
-
创建、签署和序列化
<saml2:LogoutResponse>基于RelyingPartyRegistration与刚刚注销的用户关联
添加saml2Logout新增 服务商 下线 能力。
因为它是一项可选功能,所以您需要为每个人启用它RelyingPartyRegistration.
您可以通过设置RelyingPartyRegistration.Builder#singleLogoutServiceLocation财产。 |
配置 Logout 端点
有三种行为可以由不同的终端节点触发:
-
RP 发起的注销,允许经过身份验证的用户
POST并通过向断言方发送<saml2:LogoutRequest> -
AP 发起的注销,允许断言方发送
<saml2:LogoutRequest>前往应用 -
AP 注销响应,允许断言方发送
<saml2:LogoutResponse>响应 RP 发起的<saml2:LogoutRequest>
第一个是通过执行 normal 触发的POST /logout当 principal 的类型为Saml2AuthenticatedPrincipal.
第二个是通过 POST 到/logout/saml2/slo端点,其中SAMLRequest由主张方签名。
第三个是通过 POST 到/logout/saml2/slo端点,其中SAMLResponse由主张方签名。
由于用户已登录或原始注销请求已知,因此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
要添加其他值,您可以使用 delegation,如下所示:
@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;
}
然后,您可以提供您的自定义Saml2LogoutRequestResolver在 DSL 中,如下所示:
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
要添加其他值,您可以使用 delegation,如下所示:
@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;
}
然后,您可以提供您的自定义Saml2LogoutResponseResolver在 DSL 中,如下所示:
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
}
}
然后,您可以提供您的自定义Saml2LogoutRequestValidator在 DSL 中,如下所示:
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
}
}
然后,您可以提供您的自定义Saml2LogoutResponseValidator在 DSL 中,如下所示:
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)
)
);