生成 <saml2:AuthnRequest>
如前所述,Spring Security 的 SAML 2.0 支援會生成一個 <saml2:AuthnRequest> 以便與宣告方開始身份驗證。
Spring Security 部分透過在過濾器鏈中註冊 Saml2WebSsoAuthenticationRequestFilter 來實現這一點。此過濾器預設響應端點 /saml2/authenticate/{registrationId} 和 /saml2/authenticate?registrationId={registrationId}。
例如,如果您部署到 rp.example.com,並且您將您的註冊 ID 設定為 okta,您可以導航到
結果將是一個重定向,其中包含一個 SAMLRequest 引數,該引數包含已簽名、解壓縮和編碼的 <saml2:AuthnRequest>。
配置 <saml2:AuthnRequest> 端點
要將端點配置為與預設值不同,您可以在 saml2Login 中設定值
-
Java
-
Kotlin
@Bean
SecurityFilterChain filterChain(HttpSecurity http) {
http
.saml2Login((saml2) -> saml2
.authenticationRequestUriQuery("/custom/auth/sso?peerEntityID={registrationId}")
);
return new CustomSaml2AuthenticationRequestRepository();
}
@Bean
fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
saml2Login {
authenticationRequestUriQuery = "/custom/auth/sso?peerEntityID={registrationId}"
}
}
return CustomSaml2AuthenticationRequestRepository()
}
更改 <saml2:AuthnRequest> 的儲存方式
Saml2WebSsoAuthenticationRequestFilter 使用 Saml2AuthenticationRequestRepository 來持久化 AbstractSaml2AuthenticationRequest 例項,然後 將 <saml2:AuthnRequest> 傳送 給宣告方。
此外,Saml2WebSsoAuthenticationFilter 和 Saml2AuthenticationTokenConverter 使用 Saml2AuthenticationRequestRepository 來載入任何 AbstractSaml2AuthenticationRequest 作為 驗證 <saml2:Response> 的一部分。
預設情況下,Spring Security 使用 HttpSessionSaml2AuthenticationRequestRepository,它將 AbstractSaml2AuthenticationRequest 儲存在 HttpSession 中。
如果您有 Saml2AuthenticationRequestRepository 的自定義實現,您可以透過將其公開為 @Bean 來配置它,如以下示例所示
-
Java
-
Kotlin
@Bean
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
return new CustomSaml2AuthenticationRequestRepository();
}
@Bean
open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
return CustomSaml2AuthenticationRequestRepository()
}
按中繼狀態快取 <saml2:AuthnRequest>
如果您不想使用會話來儲存 <saml2:AuthnRequest>,您也可以將其儲存在分散式快取中。如果您嘗試使用 SameSite=Strict 並且在從身份提供商重定向時丟失了身份驗證請求,這會很有幫助。
|
重要的是要記住,將它儲存在會話中有安全優勢。其中一個好處是它提供了自然的登入固定防禦。例如,如果應用程式從會話中查詢身份驗證請求,那麼即使攻擊者向受害者提供了他們自己的 SAML 響應,登入也會失敗。 另一方面,如果我們信任 InResponseTo 或 RelayState 來檢索身份驗證請求,那麼就無法知道 SAML 響應是否由該握手請求。 |
為了解決這個問題,Spring Security 提供了 CacheSaml2AuthenticationRequestRepository,您可以將其作為 bean 釋出,供過濾器鏈使用
-
Java
-
Kotlin
@Bean
Saml2AuthenticationRequestRepository<?> authenticationRequestRepository() {
return new CacheSaml2AuthenticationRequestRepository();
}
@Bean
fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<*> {
return CacheSaml2AuthenticationRequestRepository()
}
更改 <saml2:AuthnRequest> 的傳送方式
預設情況下,Spring Security 會簽署每個 <saml2:AuthnRequest> 並將其作為 GET 請求傳送給宣告方。
許多宣告方不需要已簽名的 <saml2:AuthnRequest>。這可以透過 RelyingPartyRegistrations 自動配置,或者您可以手動提供,如下所示
-
引導
-
Java
-
Kotlin
spring:
security:
saml2:
relyingparty:
registration:
okta:
assertingparty:
entity-id: ...
singlesignon.sign-request: false
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyMetadata((party) -> party
// ...
.wantAuthnRequestsSigned(false)
)
.build();
var relyingPartyRegistration: RelyingPartyRegistration =
RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
// ...
.wantAuthnRequestsSigned(false)
}
.build()
否則,您需要為 RelyingPartyRegistration#signingX509Credentials 指定私鑰,以便 Spring Security 可以在傳送之前簽署 <saml2:AuthnRequest>。
預設情況下,Spring Security 將使用 rsa-sha256 簽署 <saml2:AuthnRequest>,儘管某些宣告方將需要不同的演算法,如其元資料中所示。
您可以根據宣告方的 元資料使用 RelyingPartyRegistrations 配置演算法。
或者,您可以手動提供它
-
Java
-
Kotlin
String metadataLocation = "classpath:asserting-party-metadata.xml";
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
// ...
.assertingPartyMetadata((party) -> party
// ...
.signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
)
.build();
var metadataLocation = "classpath:asserting-party-metadata.xml"
var relyingPartyRegistration: RelyingPartyRegistration =
RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
// ...
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
// ...
.signingAlgorithms { sign: MutableList<String?> ->
sign.add(
SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
)
}
}
.build()
上面的程式碼片段使用 OpenSAML SignatureConstants 類提供演算法名稱。但這只是為了方便。由於資料型別是 String,您可以直接提供演算法的名稱。 |
一些宣告方要求 <saml2:AuthnRequest> 以 POST 方式傳送。這可以透過 RelyingPartyRegistrations 自動配置,或者您可以手動提供,如下所示
-
Java
-
Kotlin
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyMetadata((party) -> party
// ...
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
)
.build();
var relyingPartyRegistration: RelyingPartyRegistration? =
RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyMetadata { party: AssertingPartyMetadata.Builder -> party
// ...
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
}
.build()
自定義 OpenSAML 的 AuthnRequest 例項
您可能希望調整 AuthnRequest 的原因有很多。例如,您可能希望將 ForceAuthN 設定為 true,而 Spring Security 預設將其設定為 false。
您可以透過將 OpenSaml5AuthenticationRequestResolver 釋出為 @Bean 來自定義 OpenSAML 的 AuthnRequest 元素,如下所示
-
Java
-
Kotlin
@Bean
Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
RelyingPartyRegistrationResolver registrationResolver =
new DefaultRelyingPartyRegistrationResolver(registrations);
OpenSaml5AuthenticationRequestResolver authenticationRequestResolver =
new OpenSaml5AuthenticationRequestResolver(registrationResolver);
authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
.getAuthnRequest().setForceAuthn(true));
return authenticationRequestResolver;
}
@Bean
fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {
val registrationResolver : RelyingPartyRegistrationResolver =
new DefaultRelyingPartyRegistrationResolver(registrations)
val authenticationRequestResolver : OpenSaml5AuthenticationRequestResolver =
new OpenSaml5AuthenticationRequestResolver(registrationResolver)
authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
.getAuthnRequest().setForceAuthn(true))
return authenticationRequestResolver
}