最新の安定バージョンについては、Spring Security 6.4.2 を使用してください! |
シングルログアウトの実行
Spring Security には、RP および AP によって開始される SAML 2.0 シングルログアウトのサポートが付属しています。
簡単に言うと、Spring Security がサポートする 2 つのユースケースがあります。
RP 開始 - アプリケーションには、POST されると、ユーザーをログアウトして
saml2:LogoutRequest
をアサート側に送信するエンドポイントがあります。その後、アサート側はsaml2:LogoutResponse
を送り返し、アプリケーションが応答できるようにします。AP-Initiated- アプリケーションには、アサート側から
saml2:LogoutRequest
を受信するエンドポイントがあります。アプリケーションはその時点でログアウトを完了し、saml2:LogoutResponse
をアサート側に送信します。
AP が開始するシナリオでは、アプリケーションがログアウト後に実行するローカルリダイレクトは無効になります。アプリケーションが saml2:LogoutResponse を送信すると、ブラウザーを制御できなくなります。 |
シングルログアウトの最小構成
Spring Security の SAML 2.0 シングルログアウト機能を使用するには、次のものが必要です。
まず、アサート側は SAML 2.0 シングルログアウトをサポートする必要があります
次に、アサート側は、アプリケーションの
/logout/saml2/slo
エンドポイントにsaml2:LogoutRequest
とsaml2:LogoutResponse
に署名して POST するように構成する必要があります。第 3 に、アプリケーションには、
saml2:LogoutRequest
とsaml2:LogoutResponse
に署名するための 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 | - 次に、アプリケーションが SAMLSLO を使用してエンドユーザーをログアウトすることを希望していることを示します |
ランタイムの期待
上記の構成では、ログインしているユーザーは誰でも POST /logout
をアプリケーションに送信して、RP によって開始される SLO を実行できます。その後、アプリケーションは次のことを行います。
ユーザーをログアウトし、セッションを無効にします
Saml2LogoutRequestResolver
を使用して、現在ログインしているユーザーに関連付けられているRelyingPartyRegistration
に基づいて、<saml2:LogoutRequest>
を作成、署名、直列化します。RelyingPartyRegistration
に基づいて、アサート側にリダイレクトまたは投稿を送信しますアサート側から送信された
<saml2:LogoutResponse>
を逆直列化し、検証し、処理します構成済みの正常なログアウトエンドポイントにリダイレクトします
また、アサート側が <saml2:LogoutRequest>
を /logout/saml2/slo
に送信すると、アプリケーションは AP が開始するログアウトに参加できます。
Saml2LogoutRequestHandler
を使用して、アサート側から送信された<saml2:LogoutRequest>
を逆直列化し、検証し、処理します。ユーザーをログアウトし、セッションを無効にします
ログアウトしたばかりのユーザーに関連付けられた
RelyingPartyRegistration
に基づいて、<saml2:LogoutResponse>
を作成、署名、直列化します。RelyingPartyRegistration
に基づいて、アサート側にリダイレクトまたは投稿を送信します
saml2Logout を追加すると、サービスプロバイダーにログアウトする機能が追加されます。これはオプション機能であるため、個々の RelyingPartyRegistration ごとに有効にする必要があります。これを行うには、RelyingPartyRegistration.Builder#singleLogoutServiceLocation プロパティを設定します。 |
ログアウトエンドポイントの構成
さまざまなエンドポイントによってトリガーされる可能性のある動作は 3 つあります。
RP によって開始されるログアウト。これにより、認証されたユーザーは
POST
を実行し、アサート側に<saml2:LogoutRequest>
を送信してログアウトプロセスをトリガーできます。AP が開始するログアウト。これにより、アサート側が
<saml2:LogoutRequest>
をアプリケーションに送信できるようになります。AP ログアウトレスポンス。これにより、アサート側は RP によって開始された
<saml2:LogoutRequest>
にレスポンスして<saml2:LogoutResponse>
を送信できます。
1 つ目は、プリンシパルが Saml2AuthenticatedPrincipal
型の場合に、通常の POST /logout
を実行することによってトリガーされます。
2 つ目は、アサート側によって署名された SAMLRequest
を使用して /logout/saml2/slo
エンドポイントに POST することによってトリガーされます。
3 つ目は、アサート側によって署名された SAMLResponse
を使用して /logout/saml2/slo
エンドポイントに POST することによってトリガーされます。
ユーザーがすでにログインしているか、元のログアウトリクエストがわかっているため、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>
解決のカスタマイズ
Spring Security が提供するデフォルト以外の値を <saml2:LogoutRequest>
に設定する必要があるのが一般的です。
デフォルトでは、Spring Security は <saml2:LogoutRequest>
を発行し、以下を提供します。
Destination
属性 -RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceLocation
からID
属性 -GUID<Issuer>
要素 -RelyingPartyRegistration#getEntityId
から<NameID>
要素 -Authentication#getName
から
他の値を追加するには、次のように委譲を使用できます。
@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
を提供できます。
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestResolver(this.logoutRequestResolver)
)
);
<saml2:LogoutResponse>
解決のカスタマイズ
Spring Security が提供するデフォルト以外の値を <saml2:LogoutResponse>
に設定する必要があるのが一般的です。
デフォルトでは、Spring Security は <saml2:LogoutResponse>
を発行し、以下を提供します。
Destination
属性 -RelyingPartyRegistration#getAssertingPartyDetails#getSingleLogoutServiceResponseLocation
からID
属性 -GUID<Issuer>
要素 -RelyingPartyRegistration#getEntityId
から<Status>
要素 -SUCCESS
他の値を追加するには、次のように委譲を使用できます。
@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
を提供できます。
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
パラメーターと <saml2:LogoutResponse>
の InResponseTo
属性を検証できます。
セッション以外の場所にログアウトリクエストを保存する場合は、次のように DSL でカスタム実装を提供できます。
http
.saml2Logout((saml2) -> saml2
.logoutRequest((request) -> request
.logoutRequestRepository(myCustomLogoutRequestRepository)
)
);