Skip to content

Commit 1da383b

Browse files
committed
Add OpenSAML 5 Support
Issue gh-11658
1 parent c6d6bfd commit 1da383b

File tree

45 files changed

+10066
-277
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+10066
-277
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Map;
2323

2424
import jakarta.servlet.http.HttpServletRequest;
25+
import org.opensaml.core.Version;
2526

2627
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2728
import org.springframework.context.ApplicationContext;
@@ -35,15 +36,19 @@
3536
import org.springframework.security.core.Authentication;
3637
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
3738
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
39+
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;
3840
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
3941
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
4042
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrations;
4143
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
4244
import org.springframework.security.saml2.provider.service.web.OpenSaml4AuthenticationTokenConverter;
45+
import org.springframework.security.saml2.provider.service.web.OpenSaml5AuthenticationTokenConverter;
46+
import org.springframework.security.saml2.provider.service.web.OpenSamlAuthenticationTokenConverter;
4347
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
4448
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
4549
import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;
4650
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
51+
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;
4752
import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
4853
import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
4954
import org.springframework.security.web.AuthenticationEntryPoint;
@@ -115,6 +120,8 @@
115120
public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>>
116121
extends AbstractAuthenticationFilterConfigurer<B, Saml2LoginConfigurer<B>, Saml2WebSsoAuthenticationFilter> {
117122

123+
private static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith("5");
124+
118125
private String loginPage;
119126

120127
private String authenticationRequestUri = "/saml2/authenticate";
@@ -366,10 +373,18 @@ private Saml2AuthenticationRequestResolver getAuthenticationRequestResolver(B ht
366373
if (bean != null) {
367374
return bean;
368375
}
369-
OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
370-
relyingPartyRegistrationRepository(http));
371-
openSaml4AuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
372-
return openSaml4AuthenticationRequestResolver;
376+
if (USE_OPENSAML_5) {
377+
OpenSaml5AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml5AuthenticationRequestResolver(
378+
relyingPartyRegistrationRepository(http));
379+
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
380+
return openSamlAuthenticationRequestResolver;
381+
}
382+
else {
383+
OpenSaml4AuthenticationRequestResolver openSamlAuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver(
384+
relyingPartyRegistrationRepository(http));
385+
openSamlAuthenticationRequestResolver.setRequestMatcher(this.authenticationRequestMatcher);
386+
return openSamlAuthenticationRequestResolver;
387+
}
373388
}
374389

375390
private AuthenticationConverter getAuthenticationConverter(B http) {
@@ -379,22 +394,45 @@ private AuthenticationConverter getAuthenticationConverter(B http) {
379394
AuthenticationConverter authenticationConverterBean = getBeanOrNull(http,
380395
Saml2AuthenticationTokenConverter.class);
381396
if (authenticationConverterBean == null) {
382-
authenticationConverterBean = getBeanOrNull(http, OpenSaml4AuthenticationTokenConverter.class);
397+
authenticationConverterBean = getBeanOrNull(http, OpenSamlAuthenticationTokenConverter.class);
383398
}
384-
if (authenticationConverterBean == null) {
385-
OpenSaml4AuthenticationTokenConverter converter = new OpenSaml4AuthenticationTokenConverter(
399+
if (authenticationConverterBean != null) {
400+
return authenticationConverterBean;
401+
}
402+
if (USE_OPENSAML_5) {
403+
authenticationConverterBean = getBeanOrNull(http, OpenSaml5AuthenticationTokenConverter.class);
404+
if (authenticationConverterBean != null) {
405+
return authenticationConverterBean;
406+
}
407+
OpenSaml5AuthenticationTokenConverter converter = new OpenSaml5AuthenticationTokenConverter(
386408
this.relyingPartyRegistrationRepository);
387409
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
388410
converter.setRequestMatcher(this.loginProcessingUrl);
389411
return converter;
390412
}
391-
return authenticationConverterBean;
413+
authenticationConverterBean = getBeanOrNull(http, OpenSaml4AuthenticationTokenConverter.class);
414+
if (authenticationConverterBean != null) {
415+
return authenticationConverterBean;
416+
}
417+
OpenSaml4AuthenticationTokenConverter converter = new OpenSaml4AuthenticationTokenConverter(
418+
this.relyingPartyRegistrationRepository);
419+
converter.setAuthenticationRequestRepository(getAuthenticationRequestRepository(http));
420+
converter.setRequestMatcher(this.loginProcessingUrl);
421+
return converter;
392422
}
393423

394424
private void registerDefaultAuthenticationProvider(B http) {
395-
OpenSaml4AuthenticationProvider provider = getBeanOrNull(http, OpenSaml4AuthenticationProvider.class);
396-
if (provider == null) {
397-
http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider()));
425+
if (USE_OPENSAML_5) {
426+
OpenSaml5AuthenticationProvider provider = getBeanOrNull(http, OpenSaml5AuthenticationProvider.class);
427+
if (provider == null) {
428+
http.authenticationProvider(postProcess(new OpenSaml5AuthenticationProvider()));
429+
}
430+
}
431+
else {
432+
OpenSaml4AuthenticationProvider provider = getBeanOrNull(http, OpenSaml4AuthenticationProvider.class);
433+
if (provider == null) {
434+
http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider()));
435+
}
398436
}
399437
}
400438

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.List;
2121

2222
import jakarta.servlet.http.HttpServletRequest;
23+
import org.opensaml.core.Version;
2324

2425
import org.springframework.context.ApplicationContext;
2526
import org.springframework.security.authentication.AuthenticationManager;
@@ -33,17 +34,23 @@
3334
import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal;
3435
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutRequestValidator;
3536
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml4LogoutResponseValidator;
37+
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutRequestValidator;
38+
import org.springframework.security.saml2.provider.service.authentication.logout.OpenSaml5LogoutResponseValidator;
3639
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequestValidator;
3740
import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponseValidator;
3841
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
3942
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
4043
import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository;
4144
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver;
42-
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver;
4345
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestValidatorParametersResolver;
46+
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver;
47+
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestResolver;
48+
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutRequestValidatorParametersResolver;
49+
import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml5LogoutResponseResolver;
4450
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter;
4551
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestRepository;
4652
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestResolver;
53+
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestValidatorParametersResolver;
4754
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseFilter;
4855
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutResponseResolver;
4956
import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2RelyingPartyInitiatedLogoutSuccessHandler;
@@ -106,6 +113,8 @@
106113
public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
107114
extends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {
108115

116+
private static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith("5");
117+
109118
private ApplicationContext context;
110119

111120
private RelyingPartyRegistrationRepository relyingPartyRegistrationRepository;
@@ -250,14 +259,26 @@ private Saml2LogoutRequestFilter createLogoutRequestProcessingFilter(
250259
RelyingPartyRegistrationRepository registrations) {
251260
LogoutHandler[] logoutHandlers = this.logoutHandlers.toArray(new LogoutHandler[0]);
252261
Saml2LogoutResponseResolver logoutResponseResolver = createSaml2LogoutResponseResolver(registrations);
262+
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(
263+
createSaml2LogoutResponseParametersResolver(registrations),
264+
this.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);
265+
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
266+
return postProcess(filter);
267+
}
268+
269+
private Saml2LogoutRequestValidatorParametersResolver createSaml2LogoutResponseParametersResolver(
270+
RelyingPartyRegistrationRepository registrations) {
253271
RequestMatcher requestMatcher = createLogoutRequestMatcher();
272+
if (USE_OPENSAML_5) {
273+
OpenSaml5LogoutRequestValidatorParametersResolver parameters = new OpenSaml5LogoutRequestValidatorParametersResolver(
274+
registrations);
275+
parameters.setRequestMatcher(requestMatcher);
276+
return parameters;
277+
}
254278
OpenSaml4LogoutRequestValidatorParametersResolver parameters = new OpenSaml4LogoutRequestValidatorParametersResolver(
255279
registrations);
256280
parameters.setRequestMatcher(requestMatcher);
257-
Saml2LogoutRequestFilter filter = new Saml2LogoutRequestFilter(parameters,
258-
this.logoutRequestConfigurer.logoutRequestValidator(), logoutResponseResolver, logoutHandlers);
259-
filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
260-
return postProcess(filter);
281+
return parameters;
261282
}
262283

263284
private Saml2LogoutResponseFilter createLogoutResponseProcessingFilter(
@@ -397,16 +418,22 @@ public Saml2LogoutConfigurer<H> and() {
397418
}
398419

399420
private Saml2LogoutRequestValidator logoutRequestValidator() {
400-
if (this.logoutRequestValidator == null) {
401-
return new OpenSaml4LogoutRequestValidator();
421+
if (this.logoutRequestValidator != null) {
422+
return this.logoutRequestValidator;
402423
}
403-
return this.logoutRequestValidator;
424+
if (USE_OPENSAML_5) {
425+
return new OpenSaml5LogoutRequestValidator();
426+
}
427+
return new OpenSaml4LogoutRequestValidator();
404428
}
405429

406430
private Saml2LogoutRequestResolver logoutRequestResolver(RelyingPartyRegistrationRepository registrations) {
407431
if (this.logoutRequestResolver != null) {
408432
return this.logoutRequestResolver;
409433
}
434+
if (USE_OPENSAML_5) {
435+
return new OpenSaml5LogoutRequestResolver(registrations);
436+
}
410437
return new OpenSaml4LogoutRequestResolver(registrations);
411438
}
412439

@@ -473,17 +500,23 @@ public Saml2LogoutConfigurer<H> and() {
473500
}
474501

475502
private Saml2LogoutResponseValidator logoutResponseValidator() {
476-
if (this.logoutResponseValidator == null) {
477-
return new OpenSaml4LogoutResponseValidator();
503+
if (this.logoutResponseValidator != null) {
504+
return this.logoutResponseValidator;
478505
}
479-
return this.logoutResponseValidator;
506+
if (USE_OPENSAML_5) {
507+
return new OpenSaml5LogoutResponseValidator();
508+
}
509+
return new OpenSaml4LogoutResponseValidator();
480510
}
481511

482512
private Saml2LogoutResponseResolver logoutResponseResolver(RelyingPartyRegistrationRepository registrations) {
483-
if (this.logoutResponseResolver == null) {
484-
return new OpenSaml4LogoutResponseResolver(registrations);
513+
if (this.logoutResponseResolver != null) {
514+
return this.logoutResponseResolver;
515+
}
516+
if (USE_OPENSAML_5) {
517+
return new OpenSaml5LogoutResponseResolver(registrations);
485518
}
486-
return this.logoutResponseResolver;
519+
return new OpenSaml4LogoutResponseResolver(registrations);
487520
}
488521

489522
}

config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2MetadataConfigurer.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
import java.util.function.Function;
2020

21+
import org.opensaml.core.Version;
22+
2123
import org.springframework.context.ApplicationContext;
2224
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
2325
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
2426
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
2527
import org.springframework.security.saml2.provider.service.metadata.OpenSaml4MetadataResolver;
28+
import org.springframework.security.saml2.provider.service.metadata.OpenSaml5MetadataResolver;
2629
import org.springframework.security.saml2.provider.service.metadata.Saml2MetadataResponseResolver;
2730
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
2831
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
@@ -73,6 +76,8 @@
7376
public class Saml2MetadataConfigurer<H extends HttpSecurityBuilder<H>>
7477
extends AbstractHttpConfigurer<Saml2LogoutConfigurer<H>, H> {
7578

79+
private static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith("5");
80+
7681
private final ApplicationContext context;
7782

7883
private Function<RelyingPartyRegistrationRepository, Saml2MetadataResponseResolver> metadataResponseResolver;
@@ -103,6 +108,12 @@ public Saml2MetadataConfigurer(ApplicationContext context) {
103108
public Saml2MetadataConfigurer<H> metadataUrl(String metadataUrl) {
104109
Assert.hasText(metadataUrl, "metadataUrl cannot be empty");
105110
this.metadataResponseResolver = (registrations) -> {
111+
if (USE_OPENSAML_5) {
112+
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(
113+
registrations, new OpenSaml5MetadataResolver());
114+
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
115+
return metadata;
116+
}
106117
RequestMatcherMetadataResponseResolver metadata = new RequestMatcherMetadataResponseResolver(registrations,
107118
new OpenSaml4MetadataResolver());
108119
metadata.setRequestMatcher(new AntPathRequestMatcher(metadataUrl));
@@ -143,6 +154,9 @@ private Saml2MetadataResponseResolver createMetadataResponseResolver(H http) {
143154
return metadataResponseResolver;
144155
}
145156
RelyingPartyRegistrationRepository registrations = getRelyingPartyRegistrationRepository(http);
157+
if (USE_OPENSAML_5) {
158+
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSaml5MetadataResolver());
159+
}
146160
return new RequestMatcherMetadataResponseResolver(registrations, new OpenSaml4MetadataResolver());
147161
}
148162

config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParserUtils.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,22 @@
1616

1717
package org.springframework.security.config.http;
1818

19+
import org.opensaml.core.Version;
1920
import org.w3c.dom.Element;
2021

2122
import org.springframework.beans.BeanMetadataElement;
2223
import org.springframework.beans.factory.config.BeanDefinition;
2324
import org.springframework.beans.factory.config.RuntimeBeanReference;
2425
import org.springframework.beans.factory.support.AbstractBeanDefinition;
2526
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
27+
import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider;
28+
import org.springframework.security.saml2.provider.service.authentication.OpenSaml5AuthenticationProvider;
2629
import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
2730
import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
2831
import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
2932
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
33+
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
34+
import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml5AuthenticationRequestResolver;
3035
import org.springframework.util.StringUtils;
3136

3237
/**
@@ -35,6 +40,8 @@
3540
*/
3641
final class Saml2LoginBeanDefinitionParserUtils {
3742

43+
private static final boolean USE_OPENSAML_5 = Version.getVersion().startsWith("5");
44+
3845
private static final String ATT_RELYING_PARTY_REGISTRATION_REPOSITORY_REF = "relying-party-registration-repository-ref";
3946

4047
private static final String ATT_AUTHENTICATION_REQUEST_REPOSITORY_REF = "authentication-request-repository-ref";
@@ -78,16 +85,21 @@ static BeanMetadataElement createDefaultAuthenticationRequestResolver(
7885
.rootBeanDefinition(DefaultRelyingPartyRegistrationResolver.class)
7986
.addConstructorArgValue(relyingPartyRegistrationRepository)
8087
.getBeanDefinition();
81-
return BeanDefinitionBuilder.rootBeanDefinition(
82-
"org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver")
88+
if (USE_OPENSAML_5) {
89+
return BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5AuthenticationRequestResolver.class)
90+
.addConstructorArgValue(defaultRelyingPartyRegistrationResolver)
91+
.getBeanDefinition();
92+
}
93+
return BeanDefinitionBuilder.rootBeanDefinition(OpenSaml4AuthenticationRequestResolver.class)
8394
.addConstructorArgValue(defaultRelyingPartyRegistrationResolver)
8495
.getBeanDefinition();
8596
}
8697

8798
static BeanDefinition createAuthenticationProvider() {
88-
return BeanDefinitionBuilder.rootBeanDefinition(
89-
"org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider")
90-
.getBeanDefinition();
99+
if (USE_OPENSAML_5) {
100+
return BeanDefinitionBuilder.rootBeanDefinition(OpenSaml5AuthenticationProvider.class).getBeanDefinition();
101+
}
102+
return BeanDefinitionBuilder.rootBeanDefinition(OpenSaml4AuthenticationProvider.class).getBeanDefinition();
91103
}
92104

93105
static BeanMetadataElement getAuthenticationConverter(Element element) {

0 commit comments

Comments
 (0)