# 产生<saml2:AuthnRequest>
s
如前所述, Spring Security的SAML2.0支持生成一个<saml2:AuthnRequest>
,以开始与主张方进行身份验证。
Spring 安全性在一定程度上通过在过滤器链中注册Saml2WebSsoAuthenticationRequestFilter
来实现这一点。默认情况下,此筛选器响应端点/saml2/authenticate/{registrationId}
。
例如,如果你被部署到[https://rp.example.com](https://rp.example.com)
,并且你的注册ID为okta
,那么你可以导航到:
[https://rp.example.org/saml2/authenticate/ping](https://rp.example.org/saml2/authenticate/ping)
结果将是一个重定向,该重定向包含一个SAMLRequest
参数,该参数包含有符号的、已泄气的和编码的<saml2:AuthnRequest>
。
# 更改<saml2:AuthnRequest>
的存储方式
Saml2WebSsoAuthenticationRequestFilter
使用Saml2AuthenticationRequestRepository
在[将<saml2:AuthnRequest>
](# Servlet-saml2login-sp-initiated-factory)发送给断言方前持久化AbstractSaml2AuthenticationRequest
实例。
此外,Saml2WebSsoAuthenticationFilter
和Saml2AuthenticationTokenConverter
使用Saml2AuthenticationRequestRepository
来加载任何AbstractSaml2AuthenticationRequest
作为[验证<saml2:Response>
](authentication.html# Servlet-saml2login-attenticate-responses)的一部分。
默认情况下, Spring Security使用HttpSessionSaml2AuthenticationRequestRepository
,它将AbstractSaml2AuthenticationRequest
存储在HttpSession
中。
如果你有Saml2AuthenticationRequestRepository
的自定义实现,则可以通过将其公开为@Bean
来配置它,如以下示例所示:
Java
@Bean
Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository() {
return new CustomSaml2AuthenticationRequestRepository();
}
Kotlin
@Bean
open fun authenticationRequestRepository(): Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
return CustomSaml2AuthenticationRequestRepository()
}
# 更改<saml2:AuthnRequest>
的发送方式
默认情况下, Spring Security对每个<saml2:AuthnRequest>
进行签名,并将其作为get发送给主张方。
许多有主张的当事人不需要签署<saml2:AuthnRequest>
。这可以通过RelyingPartyRegistrations
自动配置,也可以手动提供,例如:
例1.不需要签名的authnrequests
引导
spring:
security:
saml2:
relyingparty:
okta:
identityprovider:
entity-id: ...
singlesignon.sign-request: false
Java
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyDetails(party -> party
// ...
.wantAuthnRequestsSigned(false)
)
.build();
Kotlin
var relyingPartyRegistration: RelyingPartyRegistration =
RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
// ...
.wantAuthnRequestsSigned(false)
}
.build();
否则,你将需要为RelyingPartyRegistration#signingX509Credentials
指定一个私钥,以便 Spring Security可以在发送之前对<saml2:AuthnRequest>
进行签名。
默认情况下, Spring Security将使用rsa-sha256
对<saml2:AuthnRequest>
进行签名,尽管一些主张方将需要不同的算法,如其元数据中所示。
你可以基于断言一方的[元数据使用RelyingPartyRegistrations
]来配置算法(Overview.html# Servlet-SAML2Login-RelyingPartyRegistrationRepository)。
或者,你可以手动提供它:
Java
String metadataLocation = "classpath:asserting-party-metadata.xml";
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
// ...
.assertingPartyDetails((party) -> party
// ...
.signingAlgorithms((sign) -> sign.add(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512))
)
.build();
Kotlin
var metadataLocation = "classpath:asserting-party-metadata.xml"
var relyingPartyRegistration: RelyingPartyRegistration =
RelyingPartyRegistrations.fromMetadataLocation(metadataLocation)
// ...
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
// ...
.signingAlgorithms { sign: MutableList<String?> ->
sign.add(
SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512
)
}
}
.build();
上面的代码片段使用OpenSAMLSignatureConstants 类来提供算法名称。但是,这只是为了方便。 因为数据类型是 String ,所以你可以直接提供算法的名称。 |
---|
一些主张的缔约方要求张贴<saml2:AuthnRequest>
。这可以通过RelyingPartyRegistrations
自动配置,也可以手动提供,例如:
Java
RelyingPartyRegistration relyingPartyRegistration = RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyDetails(party -> party
// ...
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
)
.build();
Kotlin
var relyingPartyRegistration: RelyingPartyRegistration? =
RelyingPartyRegistration.withRegistrationId("okta")
// ...
.assertingPartyDetails { party: AssertingPartyDetails.Builder -> party
// ...
.singleSignOnServiceBinding(Saml2MessageBinding.POST)
}
.build()
# 定制OpenSAML的AuthnRequest
实例
你可能需要调整AuthnRequest
的原因有很多。例如,你可能希望将ForceAuthN
设置为true
,而 Spring Security将其默认设置为false
。
如果你不需要来自HttpServletRequest
的信息来做出决定,那么最简单的方法是[用OpenSAML注册一个自定义AuthnRequestMarshaller
](overview.html# Servlet-saml2login-opensaml-customization)。这将使你能够在序列化之前访问后处理AuthnRequest
实例。
但是,如果你确实需要来自该请求的某些内容,那么你可以使用创建一个自定义Saml2AuthenticationRequestContext
实现,然后创建一个Converter<Saml2AuthenticationRequestContext, AuthnRequest>
来构建自己的AuthnRequest
,就像这样:
Java
@Component
public class AuthnRequestConverter implements
Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
private final AuthnRequestBuilder authnRequestBuilder;
private final IssuerBuilder issuerBuilder;
// ... constructor
public AuthnRequest convert(Saml2AuthenticationRequestContext context) {
MySaml2AuthenticationRequestContext myContext = (MySaml2AuthenticationRequestContext) context;
Issuer issuer = issuerBuilder.buildObject();
issuer.setValue(myContext.getIssuer());
AuthnRequest authnRequest = authnRequestBuilder.buildObject();
authnRequest.setIssuer(issuer);
authnRequest.setDestination(myContext.getDestination());
authnRequest.setAssertionConsumerServiceURL(myContext.getAssertionConsumerServiceUrl());
// ... additional settings
authRequest.setForceAuthn(myContext.getForceAuthn());
return authnRequest;
}
}
Kotlin
@Component
class AuthnRequestConverter : Converter<Saml2AuthenticationRequestContext, AuthnRequest> {
private val authnRequestBuilder: AuthnRequestBuilder? = null
private val issuerBuilder: IssuerBuilder? = null
// ... constructor
override fun convert(context: Saml2AuthenticationRequestContext): AuthnRequest {
val myContext: MySaml2AuthenticationRequestContext = context
val issuer: Issuer = issuerBuilder.buildObject()
issuer.value = myContext.getIssuer()
val authnRequest: AuthnRequest = authnRequestBuilder.buildObject()
authnRequest.issuer = issuer
authnRequest.destination = myContext.getDestination()
authnRequest.assertionConsumerServiceURL = myContext.getAssertionConsumerServiceUrl()
// ... additional settings
authRequest.setForceAuthn(myContext.getForceAuthn())
return authnRequest
}
}
然后,可以构造自己的Saml2AuthenticationRequestContextResolver
和Saml2AuthenticationRequestFactory
,并将它们发布为@Bean
s:
Java
@Bean
Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver() {
Saml2AuthenticationRequestContextResolver resolver =
new DefaultSaml2AuthenticationRequestContextResolver();
return request -> {
Saml2AuthenticationRequestContext context = resolver.resolve(request);
return new MySaml2AuthenticationRequestContext(context, request.getParameter("force") != null);
};
}
@Bean
Saml2AuthenticationRequestFactory authenticationRequestFactory(
AuthnRequestConverter authnRequestConverter) {
OpenSaml4AuthenticationRequestFactory authenticationRequestFactory =
new OpenSaml4AuthenticationRequestFactory();
authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter);
return authenticationRequestFactory;
}
Kotlin
@Bean
open fun authenticationRequestContextResolver(): Saml2AuthenticationRequestContextResolver {
val resolver: Saml2AuthenticationRequestContextResolver = DefaultSaml2AuthenticationRequestContextResolver()
return Saml2AuthenticationRequestContextResolver { request: HttpServletRequest ->
val context = resolver.resolve(request)
MySaml2AuthenticationRequestContext(
context,
request.getParameter("force") != null
)
}
}
@Bean
open fun authenticationRequestFactory(
authnRequestConverter: AuthnRequestConverter?
): Saml2AuthenticationRequestFactory? {
val authenticationRequestFactory = OpenSaml4AuthenticationRequestFactory()
authenticationRequestFactory.setAuthenticationRequestContextConverter(authnRequestConverter)
return authenticationRequestFactory
}