对于最新的稳定版本,请使用 Spring Security 6.4.3! |
SAML 迁移
以下步骤与有关如何配置 SAML 2.0 的更改有关。
使用 OpenSAML 4
OpenSAML 3 的生命周期已结束。 因此,Spring Security 6 放弃了对它的支持,将其 OpenSAML 基线提高到 4。
要准备升级,请将 pom 更新为依赖于 OpenSAML 4 而不是 3:
-
Maven
-
Gradle
<dependencyManagement>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-core</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-api</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>org.opensaml</groupId>
<artifactId>opensaml-saml-impl</artifactId>
<version>4.2.1</version>
</dependency>
</dependencyManagement>
dependencies {
constraints {
api "org.opensaml:opensaml-core:4.2.1"
api "org.opensaml:opensaml-saml-api:4.2.1"
api "org.opensaml:opensaml-saml-impl:4.2.1"
}
}
您必须至少使用 OpenSAML 4.1.1 才能更新到 Spring Security 6 的 SAML 支持。
用OpenSaml4AuthenticationProvider
为了同时支持 OpenSAML 3 和 OpenSAML 4,Spring Security 发布了OpenSamlAuthenticationProvider
和OpenSaml4AuthenticationProvider
.
在 6.0 中,由于删除了 OpenSAML3 支持,因此OpenSamlAuthenticationProvider
也会被删除。
并非 中的所有方法OpenSamlAuthenticationProvider
被 1 对 1 移植到OpenSaml4AuthenticationProvider
.
因此,需要进行一些调整才能进行挑战。
考虑以下OpenSamlAuthenticationProvider
:
-
Java
-
Kotlin
OpenSamlAuthenticationProvider versionThree = new OpenSamlAuthenticationProvider();
versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor);
versionThree.setResponseTimeValidationSkew(myDuration);
val versionThree: OpenSamlAuthenticationProvider = OpenSamlAuthenticationProvider()
versionThree.setAuthoritiesExtractor(myAuthoritiesExtractor)
versionThree.setResponseTimeValidationSkew(myDuration)
这应该更改为:
-
Java
-
Kotlin
Converter<ResponseToken, Saml2Authentication> delegate = OpenSaml4AuthenticationProvider
.createDefaultResponseAuthenticationConverter();
OpenSaml4AuthenticationProvider versionFour = new OpenSaml4AuthenticationProvider();
versionFour.setResponseAuthenticationConverter((responseToken) -> {
Saml2Authentication authentication = delegate.convert(responseToken);
Assertion assertion = responseToken.getResponse().getAssertions().get(0);
AuthenticatedPrincipal principal = (AuthenticatedPrincipal) authentication.getPrincipal();
Collection<GrantedAuthority> authorities = myAuthoritiesExtractor.convert(assertion);
return new Saml2Authentication(principal, authentication.getSaml2Response(), authorities);
});
Converter<AssertionToken, Saml2ResponseValidationResult> validator = OpenSaml4AuthenticationProvider
.createDefaultAssertionValidatorWithParameters((p) -> p.put(CLOCK_SKEW, myDuration));
versionFour.setAssertionValidator(validator);
val delegate = OpenSaml4AuthenticationProvider.createDefaultResponseAuthenticationConverter()
val versionFour = OpenSaml4AuthenticationProvider()
versionFour.setResponseAuthenticationConverter({
responseToken -> {
val authentication = delegate.convert(responseToken)
val assertion = responseToken.getResponse().getAssertions().get(0)
val principal = (AuthenticatedPrincipal) authentication.getPrincipal()
val authorities = myAuthoritiesExtractor.convert(assertion)
return Saml2Authentication(principal, authentication.getSaml2Response(), authorities)
}
})
val validator = OpenSaml4AuthenticationProvider
.createDefaultAssertionValidatorWithParameters({ p -> p.put(CLOCK_SKEW, myDuration) })
versionFour.setAssertionValidator(validator)
停止使用 SAML 2.0Converter
构造 函数
在 Spring Security 的 SAML 2.0 支持的早期版本中,Saml2MetadataFilter
和Saml2AuthenticationTokenConverter
随Converter
.
这种抽象级别使得发展类变得很棘手,因此需要一个专用的接口RelyingPartyRegistrationResolver
在更高版本中引入。
在 6.0 中,Converter
构造函数被删除。
要在 5.8 中为此做好准备,请更改实现Converter<HttpServletRequest, RelyingPartyRegistration>
改为实现RelyingPartyRegistrationResolver
.
更改为使用Saml2AuthenticationRequestResolver
Saml2AuthenticationContextResolver
和Saml2AuthenticationRequestFactory
在 6.0 中删除,就像Saml2WebSsoAuthenticationRequestFilter
这需要他们。
它们被替换为Saml2AuthenticationRequestResolver
和一个新的构造函数Saml2WebSsoAuthenticationRequestFilter
.
新接口删除了两个类之间不必要的传输对象。
大多数应用程序不需要执行任何作;但是,如果您使用或配置Saml2AuthenticationRequestContextResolver
或Saml2AuthenticationRequestFactory
,请尝试以下步骤来转换,而不是使用Saml2AuthenticationRequestResolver
.
用setAuthnRequestCustomizer
而不是setAuthenticationRequestContextConverter
如果您正在调用OpenSaml4AuthenticationReqeustFactory#setAuthenticationRequestContextConverter
,例如:
-
Java
@Bean
Saml2AuthenticationRequestFactory authenticationRequestFactory() {
OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
factory.setAuthenticationRequestContextConverter((context) -> {
AuthnRequestBuilder authnRequestBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
.getBuilderFactory().getBuilder(AuthnRequest.DEFAULT_ELEMENT_NAME);
IssuerBuilder issuerBuilder = ConfigurationService.get(XMLObjectProviderRegistry.class)
.getBuilderFactory().getBuilder(Issuer.DEFAULT_ELEMENT_NAME);
tring issuer = context.getIssuer();
String destination = context.getDestination();
String assertionConsumerServiceUrl = context.getAssertionConsumerServiceUrl();
String protocolBinding = context.getRelyingPartyRegistration().getAssertionConsumerServiceBinding().getUrn();
AuthnRequest auth = authnRequestBuilder.buildObject();
auth.setID("ARQ" + UUID.randomUUID().toString().substring(1));
auth.setIssueInstant(Instant.now());
auth.setForceAuthn(Boolean.TRUE);
auth.setIsPassive(Boolean.FALSE);
auth.setProtocolBinding(SAMLConstants.SAML2_POST_BINDING_URI);
Issuer iss = issuerBuilder.buildObject();
iss.setValue(issuer);
auth.setIssuer(iss);
auth.setDestination(destination);
auth.setAssertionConsumerServiceURL(assertionConsumerServiceUrl);
});
return factory;
}
以确保 ForceAuthn 设置为true
,您可以改为执行以下作:
-
Java
@Bean
Saml2AuthenticationRequestResolver authenticationRequestResolver(RelyingPartyRegistrationResolver registrations) {
OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest().setForceAuthn(Boolean.TRUE));
return resolver;
}
此外,由于setAuthnRequestCustomizer
可以直接访问HttpServletRequest
,则不需要Saml2AuthenticationRequestContextResolver
.
简单使用setAuthnRequestCustomizer
直接从HttpServletRequest
您需要的此信息。
用setAuthnRequestCustomizer
而不是setProtocolBinding
而不是执行:
-
Java
@Bean
Saml2AuthenticationRequestFactory authenticationRequestFactory() {
OpenSaml4AuthenticationRequestFactory factory = new OpenSaml4AuthenticationRequestFactory();
factory.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST")
return factory;
}
您可以执行以下作:
-
Java
@Bean
Saml2AuthenticationRequestResolver authenticationRequestResolver() {
OpenSaml4AuthenticationRequestResolver reaolver = new OpenSaml4AuthenticationRequestResolver(registrations);
resolver.setAuthnRequestCustomizer((context) -> context.getAuthnRequest()
.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"));
return resolver;
}
由于 Spring Security 仅支持 |
使用最新的Saml2AuthenticationToken
构造 函数
在早期版本中,Saml2AuthenticationToken
将几个单独的设置作为构造函数参数。
每次需要添加新参数时,这都会带来挑战。
由于这些设置中的大多数都是RelyingPartyRegistration
中,添加了一个新的构造函数,其中RelyingPartyRegistration
,使构造函数更稳定。
它还在于它与OAuth2LoginAuthenticationToken
.
大多数应用程序不会直接构造此类,因为Saml2WebSsoAuthenticationFilter
确实。
但是,如果您的应用程序构造了一个,请从以下位置更改:
-
Java
-
Kotlin
new Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
Saml2AuthenticationToken(saml2Response, registration.getSingleSignOnServiceLocation(),
registration.getAssertingParty().getEntityId(), registration.getEntityId(), registration.getCredentials())
自:
-
Java
-
Kotlin
new Saml2AuthenticationToken(saml2Response, registration)
Saml2AuthenticationToken(saml2Response, registration)
用RelyingPartyRegistration
更新的方法
在 Spring Security 的 SAML 支持的早期版本中,某些RelyingPartyRegistration
方法及其功能。
随着更多功能的添加RelyingPartyRegistration
,有必要通过将方法名称更改为与 spec 语言一致的名称来澄清这种歧义。
中已弃用的方法RelyingPartyRegstration
被删除。
为此,请考虑以下RelyingPartyRegistration
:
-
Java
-
Kotlin
String idpEntityId = registration.getRemoteIdpEntityId();
String assertionConsumerServiceUrl = registration.getAssertionConsumerServiceUrlTemplate();
String idpWebSsoUrl = registration.getIdpWebSsoUrl();
String localEntityId = registration.getLocalEntityIdTemplate();
List<Saml2X509Credential> verifying = registration.getCredentials().stream()
.filter(Saml2X509Credential::isSignatureVerficationCredential)
.collect(Collectors.toList());
val idpEntityId: String = registration.getRemoteIdpEntityId()
val assertionConsumerServiceUrl: String = registration.getAssertionConsumerServiceUrlTemplate()
val idpWebSsoUrl: String = registration.getIdpWebSsoUrl()
val localEntityId: String = registration.getLocalEntityIdTemplate()
val verifying: List<Saml2X509Credential> = registration.getCredentials()
.filter(Saml2X509Credential::isSignatureVerficationCredential)
这应该更改为:
-
Java
-
Kotlin
String assertingPartyEntityId = registration.getAssertingPartyDetails().getEntityId();
String assertionConsumerServiceLocation = registration.getAssertionConsumerServiceLocation();
String singleSignOnServiceLocation = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation();
String entityId = registration.getEntityId();
List<Saml2X509Credential> verifying = registration.getAssertingPartyDetails().getVerificationX509Credentials();
val assertingPartyEntityId: String = registration.getAssertingPartyDetails().getEntityId()
val assertionConsumerServiceLocation: String = registration.getAssertionConsumerServiceLocation()
val singleSignOnServiceLocation: String = registration.getAssertingPartyDetails().getSingleSignOnServiceLocation()
val entityId: String = registration.getEntityId()
val verifying: List<Saml2X509Credential> = registration.getAssertingPartyDetails().getVerificationX509Credentials()
有关所有已更改方法的完整列表,请参阅RelyingPartyRegistration
的 JavaDoc.