生成 <saml2:AuthnRequest>s

如前所述,Spring Security 的 SAML 2.0 支援會生成一個 <saml2:AuthnRequest>,用於啟動與宣告方 (asserting party) 的認證過程。

Spring Security 透過在過濾器鏈中註冊 Saml2WebSsoAuthenticationRequestFilter 來部分實現此功能。此過濾器預設響應 /saml2/authenticate/{registrationId}/saml2/authenticate?registrationId={registrationId} 這兩個端點。

例如,如果您部署到 rp.example.com 併為您的註冊指定 ID 為 okta,則可以導航至

結果將是一個重定向,其中包含一個 SAMLRequest 引數,該引數中包含了簽名、壓縮 (deflated) 並編碼後的 <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> 傳送給宣告方之前。

此外,Saml2WebSsoAuthenticationFilterSaml2AuthenticationTokenConverter 使用一個 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> 的傳送方式

預設情況下,Spring Security 會對每個 <saml2:AuthnRequest> 進行簽名,並將其作為 GET 請求傳送給宣告方。

許多宣告方不需要簽名的 <saml2:AuthnRequest>。這可以透過 RelyingPartyRegistrations 自動配置,或者您可以手動提供,如下所示

無需簽名的 AuthnRequests
  • Boot

  • Java

  • Kotlin

spring:
  security:
    saml2:
      relyingparty:
        okta:
          identityprovider:
            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> 進行簽名,但有些宣告方可能會要求不同的演算法,這在其元資料中有所說明。

或者,您可以手動提供

  • 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

您可以透過將 OpenSaml4AuthenticationRequestResolver 釋出為 @Bean 來自定義 OpenSAML 的 AuthnRequest 的元素,如下所示

  • Java

  • Kotlin

@Bean
Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationRepository registrations) {
    RelyingPartyRegistrationResolver registrationResolver =
            new DefaultRelyingPartyRegistrationResolver(registrations);
    OpenSaml4AuthenticationRequestResolver authenticationRequestResolver =
            new OpenSaml4AuthenticationRequestResolver(registrationResolver);
    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
            .getAuthnRequest().setForceAuthn(true));
    return authenticationRequestResolver;
}
@Bean
fun authenticationRequestResolver(registrations : RelyingPartyRegistrationRepository) : Saml2AuthenticationRequestResolver {
    val registrationResolver : RelyingPartyRegistrationResolver =
            new DefaultRelyingPartyRegistrationResolver(registrations)
    val authenticationRequestResolver : OpenSaml4AuthenticationRequestResolver =
            new OpenSaml4AuthenticationRequestResolver(registrationResolver)
    authenticationRequestResolver.setAuthnRequestCustomizer((context) -> context
            .getAuthnRequest().setForceAuthn(true))
    return authenticationRequestResolver
}