此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Security 6.4.1! |
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 和 4,Spring Security 发布了 和 .
在 6.0 中,由于 OpenSAML3 支持被删除,因此也被删除。OpenSamlAuthenticationProvider
OpenSaml4AuthenticationProvider
OpenSamlAuthenticationProvider
并非所有 中的方法都以 1 对 1 的方式移植到 。
因此,需要进行一些调整才能进行挑战。OpenSamlAuthenticationProvider
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.0 构造函数Converter
在 Spring Security 的 SAML 2.0 支持的早期版本中,并附带了 .
这种抽象级别使得类的演化变得很棘手,因此在以后的版本中引入了专用接口。Saml2MetadataFilter
Saml2AuthenticationTokenConverter
Converter
RelyingPartyRegistrationResolver
在 6.0 中,构造函数被删除。
要在 5.8 中为此做好准备,请将 implement 的类更改为 implement 。Converter
Converter<HttpServletRequest, RelyingPartyRegistration>
RelyingPartyRegistrationResolver
更改为使用Saml2AuthenticationRequestResolver
Saml2AuthenticationContextResolver
并在 6.0 中删除,就像需要它们一样。
它们被 和 中的新构造函数替换。
新接口删除了两个类之间不必要的传输对象。Saml2AuthenticationRequestFactory
Saml2WebSsoAuthenticationRequestFilter
Saml2AuthenticationRequestResolver
Saml2WebSsoAuthenticationRequestFilter
大多数应用程序不需要执行任何操作;但是,如果您使用 OR 配置 或 ,请尝试以下步骤来转换,而不是使用 。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
在早期版本中,将几个单独的设置作为构造函数参数。
每次需要添加新参数时,这都会带来挑战。
由于这些设置中的大多数都是 的一部分,因此在可以提供 a 的位置添加了一个新的构造函数,使构造函数更加稳定。
它的价值还在于它与 的设计更紧密地保持一致。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 支持的早期版本中,某些方法的含义及其功能存在一些歧义。
随着 中添加了更多功能,有必要通过将方法名称更改为与 spec 语言一致的名称来澄清这种歧义。RelyingPartyRegistration
RelyingPartyRegistration
中已弃用的方法将被删除。
为此,请考虑以下 的代表性用法 :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。