Performing Single Logout
Spring Security 附带了对 RP- 和 AP- 发起的 SAML 2.0 单点注销的支持。
Spring Security ships with support for RP- and AP-initiated SAML 2.0 Single Logout.
简而言之,Spring Security 支持以下两种用例:
Briefly, there are two use cases Spring Security supports:
-
RP-Initiated - Your application has an endpoint that, when POSTed to, will logout the user and send a
saml2:LogoutRequest
to the asserting party. Thereafter, the asserting party will send back asaml2:LogoutResponse
and allow your application to respond -
AP-Initiated - Your application has an endpoint that will receive a
saml2:LogoutRequest
from the asserting party. Your application will complete its logout at that point and then send asaml2:LogoutResponse
to the asserting party.
在 AP-Initiated 场景中,应用程序在退出后将要执行的任何本地重定向都将变得毫无意义。一旦应用程序发送 |
In the AP-Initiated scenario, any local redirection that your application would do post-logout is rendered moot.
Once your application sends a |
Minimal Configuration for Single Logout
要使用 Spring Security 的 SAML 2.0 单点注销功能,您需要以下内容:
To use Spring Security’s SAML 2.0 Single Logout feature, you will need the following things:
-
First, the asserting party must support SAML 2.0 Single Logout
-
Second, the asserting party should be configured to sign and POST
saml2:LogoutRequest
s andsaml2:LogoutResponse
s your application’s/logout/saml2/slo
endpoint -
Third, your application must have a PKCS#8 private key and X.509 certificate for signing
saml2:LogoutRequest
s andsaml2:LogoutResponse
s
您可以从初始最小示例开始,并添加以下配置:
You can begin from the initial minimal example and add the following configuration:
@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 | - First, add your signing key to the RelyingPartyRegistration instance or to multiple instances |
2 | - Second, indicate that your application wants to use SAML SLO to logout the end user |
Runtime Expectations
给定以上配置,任何已登录的用户都可以向您的应用程序发送 POST /logout
来执行 RP 发起的 SLO。然后,您的应用程序将执行以下操作:
Given the above configuration any logged in user can send a POST /logout
to your application to perform RP-initiated SLO.
Your application will then do the following:
-
Logout the user and invalidate the session
-
Use a
Saml2LogoutRequestResolver
to create, sign, and serialize a<saml2:LogoutRequest>
based on theRelyingPartyRegistration
associated with the currently logged-in user. -
Send a redirect or post to the asserting party based on the
RelyingPartyRegistration
-
Deserialize, verify, and process the
<saml2:LogoutResponse>
sent by the asserting party -
Redirect to any configured successful logout endpoint
此外,如果断言方向 /logout/saml2/slo
发送 <saml2:LogoutRequest>
时,您的应用程序也可以参与 AP 发起的注销操作:
Also, your application can participate in an AP-initiated logout when the asserting party sends a <saml2:LogoutRequest>
to /logout/saml2/slo
:
-
Use a
Saml2LogoutRequestHandler
to deserialize, verify, and process the<saml2:LogoutRequest>
sent by the asserting party -
Logout the user and invalidate the session
-
Create, sign, and serialize a
<saml2:LogoutResponse>
based on theRelyingPartyRegistration
associated with the just logged-out user -
Send a redirect or post to the asserting party based on the
RelyingPartyRegistration
添加 |
Adding |
Configuring Logout Endpoints
有三个行为可由不同的端点触发:
There are three behaviors that can be triggered by different endpoints:
-
RP-initiated logout, which allows an authenticated user to
POST
and trigger the logout process by sending the asserting party a<saml2:LogoutRequest>
-
AP-initiated logout, which allows an asserting party to send a
<saml2:LogoutRequest>
to the application -
AP logout response, which allows an asserting party to send a
<saml2:LogoutResponse>
in response to the RP-initiated<saml2:LogoutRequest>
第一个是在主体类型为 Saml2AuthenticatedPrincipal
时执行常规 POST /logout
触发。
The first is triggered by performing normal POST /logout
when the principal is of type Saml2AuthenticatedPrincipal
.
第二个由用宣告方签名的 SAMLRequest
POST 到 /logout/saml2/slo
终结点触发。
The second is triggered by POSTing to the /logout/saml2/slo
endpoint with a SAMLRequest
signed by the asserting party.
第三个由用宣告方签名的 SAMLResponse
POST 到 /logout/saml2/slo
终结点触发。
The third is triggered by POSTing to the /logout/saml2/slo
endpoint with a SAMLResponse
signed by the asserting party.
因为用户已经登陆或原始的登出请求是已知的,所以 registrationId
也是已知的。出于这个原因,{registrationId}
默认情况下不包含在这些 URL 中。
Because the user is already logged in or the original Logout Request is known, the registrationId
is already known.
For this reason, {registrationId}
is not part of these URLs by default.
此 URL 在 DSL 中可以自定义。
This URL is customizable in the DSL.
例如,如果你将你现有的依赖方迁移到 Spring Security 上,那么你的宣告方可能已经指向 GET /SLOService.saml2
。为了减少宣告方配置上的更改,你可以在 DSL 中如此配置过滤器:
For example, if you are migrating your existing relying party over to Spring Security, your asserting party may already be pointing to GET /SLOService.saml2
.
To reduce changes in configuration for the asserting party, you can configure the filter in the DSL like so:
-
Java
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request.logoutUrl("/SLOService.saml2"))
.logoutResponse((response) -> response.logoutUrl("/SLOService.saml2"))
);
你还应在你的 RelyingPartyRegistration
中配置这些终结点。
You should also configure these endpoints in your RelyingPartyRegistration
.
Customizing <saml2:LogoutRequest>
Resolution
通常需要在 <saml2:LogoutRequest>
中设置除 Spring Security 提供的默认值之外的其他值。
It’s common to need to set other values in the <saml2:LogoutRequest>
than the defaults that Spring Security provides.
默认情况下,Spring Security 将发出 <saml2:LogoutRequest>
并提供:
By default, Spring Security will issue a <saml2:LogoutRequest>
and supply:
-
The
Destination
attribute - fromRelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceLocation
-
The
ID
attribute - a GUID -
The
<Issuer>
element - fromRelyingPartyRegistration#getEntityId
-
The
<NameID>
element - fromAuthentication#getName
若要添加其他值,你可以使用委托,如下所示:
To add other values, you can use delegation, like so:
@Bean
Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {
OpenSaml4LogoutRequestResolver logoutRequestResolver =
new OpenSaml4LogoutRequestResolver(registrations);
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
:
Then, you can supply your custom Saml2LogoutRequestResolver
in the DSL as follows:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
)
);
Customizing <saml2:LogoutResponse>
Resolution
通常需要在 <saml2:LogoutResponse>
中设置除 Spring Security 提供的默认值之外的其他值。
It’s common to need to set other values in the <saml2:LogoutResponse>
than the defaults that Spring Security provides.
默认情况下,Spring Security 将发出 <saml2:LogoutResponse>
并提供:
By default, Spring Security will issue a <saml2:LogoutResponse>
and supply:
-
The
Destination
attribute - fromRelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceResponseLocation
-
The
ID
attribute - a GUID -
The
<Issuer>
element - fromRelyingPartyRegistration#getEntityId
-
The
<Status>
element -SUCCESS
若要添加其他值,你可以使用委托,如下所示:
To add other values, you can use delegation, like so:
@Bean
public Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {
OpenSaml4LogoutResponseResolver logoutRequestResolver =
new OpenSaml4LogoutResponseResolver(registrations);
logoutRequestResolver.setParametersConsumer((parameters) -> {
if (checkOtherPrevailingConditions(parameters.getRequest())) {
parameters.getLogoutRequest().getStatus().getStatusCode().setCode(StatusCode.PARTIAL_LOGOUT);
}
});
return logoutRequestResolver;
}
然后,你可以如此在 DSL 中提供你的自定义 Saml2LogoutResponseResolver
:
Then, you can supply your custom Saml2LogoutResponseResolver
in the DSL as follows:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
)
);
Customizing <saml2:LogoutRequest>
Authentication
若要自定义验证,你可以实现你自己的 Saml2LogoutRequestValidator
。此时,验证是最低限度的,因此你可能可以先如此委托到默认的 Saml2LogoutRequestValidator
:
To customize validation, you can implement your own Saml2LogoutRequestValidator
.
At this point, the validation is minimal, so you may be able to first delegate to the default Saml2LogoutRequestValidator
like so:
@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
:
Then, you can supply your custom Saml2LogoutRequestValidator
in the DSL as follows:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestAuthenticator(myOpenSamlLogoutRequestAuthenticator)
)
);
Customizing <saml2:LogoutResponse>
Authentication
若要自定义验证,你可以实现你自己的 Saml2LogoutResponseValidator
。此时,验证是最低限度的,因此你可能可以先如此委托到默认的 Saml2LogoutResponseValidator
:
To customize validation, you can implement your own Saml2LogoutResponseValidator
.
At this point, the validation is minimal, so you may be able to first delegate to the default Saml2LogoutResponseValidator
like so:
@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
:
Then, you can supply your custom Saml2LogoutResponseValidator
in the DSL as follows:
http
.saml2Logout((saml2) -> saml2
.logoutResponse((response) -> response
.logoutResponseAuthenticator(myOpenSamlLogoutResponseAuthenticator)
)
);
Customizing <saml2:LogoutRequest>
storage
当你的应用发送 <saml2:LogoutRequest>
时,该值储存在会话中,以便可以验证 RelayState
参数和 <saml2:LogoutResponse>
中的 InResponseTo
属性。
When your application sends a <saml2:LogoutRequest>
, the value is stored in the session so that the RelayState
parameter and the InResponseTo
attribute in the <saml2:LogoutResponse>
can be verified.
如果你想将在会话之外的某个地方存储登出请求,你可以如此在 DSL 中提供你的自定义实现:
If you want to store logout requests in some place other than the session, you can supply your custom implementation in the DSL, like so:
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestRepository(myCustomLogoutRequestRepository)
)
);