生成 <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>
发送给声明方之前。
此外,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>
的发送方式
默认情况下,Spring Security 会对每个 <saml2:AuthnRequest>
进行签名,并将其作为 GET 请求发送给声明方。
许多声明方不需要签名的 <saml2:AuthnRequest>
。这可以通过 RelyingPartyRegistrations
自动配置,或者您可以手动提供,如下所示
-
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
}