Browse Source

Move Saml2 Authentication Filters

Issue gh-8819
Josh Cummings 2 years ago
parent
commit
506e50bfd0
14 changed files with 488 additions and 385 deletions
  1. 2 2
      config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java
  2. 2 2
      config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java
  3. 2 2
      config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java
  4. 1 1
      config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java
  5. 1 1
      config/src/test/kotlin/org/springframework/security/config/web/servlet/Saml2DslTests.kt
  6. 5 6
      saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java
  7. 9 109
      saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java
  8. 9 253
      saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java
  9. 308 0
      saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilter.java
  10. 145 0
      saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java
  11. 1 1
      saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java
  12. 1 1
      saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java
  13. 1 6
      saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilterTests.java
  14. 1 1
      saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java

+ 2 - 2
config/src/main/java/org/springframework/security/config/annotation/web/builders/FilterOrderRegistration.java

@@ -85,7 +85,7 @@ final class FilterOrderRegistration {
 				"org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
 				order.next());
 		this.filterToOrder.put(
-				"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
+				"org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter",
 				order.next());
 		put(X509AuthenticationFilter.class, order.next());
 		put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
@@ -93,7 +93,7 @@ final class FilterOrderRegistration {
 		this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
 				order.next());
 		this.filterToOrder.put(
-				"org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
+				"org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter",
 				order.next());
 		put(UsernamePasswordAuthenticationFilter.class, order.next());
 		order.next(); // gh-8105

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

@@ -37,8 +37,6 @@ import org.springframework.security.saml2.provider.service.authentication.OpenSa
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter;
 import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.DefaultSaml2AuthenticationRequestContextResolver;
 import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
@@ -46,7 +44,9 @@ import org.springframework.security.saml2.provider.service.web.RelyingPartyRegis
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
+import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;
 import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 import org.springframework.security.web.AuthenticationEntryPoint;
 import org.springframework.security.web.authentication.AuthenticationConverter;
 import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint;

+ 2 - 2
config/src/main/java/org/springframework/security/config/http/Saml2LoginBeanDefinitionParser.java

@@ -39,8 +39,8 @@ import org.springframework.core.ResolvableType;
 import org.springframework.security.config.Elements;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter;
+import org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

+ 1 - 1
config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java

@@ -87,7 +87,6 @@ import org.springframework.security.saml2.provider.service.registration.InMemory
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
 import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
 import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
@@ -95,6 +94,7 @@ import org.springframework.security.saml2.provider.service.web.Saml2Authenticati
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
 import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver;
 import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 import org.springframework.security.web.FilterChainProxy;
 import org.springframework.security.web.SecurityFilterChain;
 import org.springframework.security.web.authentication.AuthenticationConverter;

+ 1 - 1
config/src/test/kotlin/org/springframework/security/config/web/servlet/Saml2DslTests.kt

@@ -41,7 +41,7 @@ import org.springframework.security.saml2.provider.service.registration.InMemory
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository
 import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter
 import org.springframework.test.web.servlet.MockMvc
 import org.springframework.test.web.servlet.get
 import org.springframework.test.web.servlet.request.MockMvcRequestBuilders

+ 5 - 6
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/Saml2AuthenticationToken.java

@@ -22,6 +22,7 @@ import java.util.List;
 import org.springframework.security.authentication.AbstractAuthenticationToken;
 import org.springframework.security.saml2.credentials.Saml2X509Credential;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 import org.springframework.util.Assert;
 
 /**
@@ -44,9 +45,8 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
 	 * Creates a {@link Saml2AuthenticationToken} with the provided parameters.
 	 *
 	 * Note that the given {@link RelyingPartyRegistration} should have all its templates
-	 * resolved at this point. See
-	 * {@link org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter}
-	 * for an example of performing that resolution.
+	 * resolved at this point. See {@link Saml2WebSsoAuthenticationFilter} for an example
+	 * of performing that resolution.
 	 * @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to
 	 * use
 	 * @param saml2Response the SAML 2.0 response to authenticate
@@ -68,9 +68,8 @@ public class Saml2AuthenticationToken extends AbstractAuthenticationToken {
 	 * Creates a {@link Saml2AuthenticationToken} with the provided parameters
 	 *
 	 * Note that the given {@link RelyingPartyRegistration} should have all its templates
-	 * resolved at this point. See
-	 * {@link org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter}
-	 * for an example of performing that resolution.
+	 * resolved at this point. See {@link Saml2WebSsoAuthenticationFilter} for an example
+	 * of performing that resolution.
 	 * @param relyingPartyRegistration the resolved {@link RelyingPartyRegistration} to
 	 * use
 	 * @param saml2Response the SAML 2.0 response to authenticate

+ 9 - 109
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilter.java

@@ -16,130 +16,30 @@
 
 package org.springframework.security.saml2.provider.service.servlet.filter;
 
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.springframework.security.authentication.AbstractAuthenticationToken;
-import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
-import org.springframework.security.saml2.core.Saml2Error;
-import org.springframework.security.saml2.core.Saml2ErrorCodes;
-import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
-import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
-import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
-import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
-import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
 import org.springframework.security.web.authentication.AuthenticationConverter;
-import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
-import org.springframework.util.Assert;
 
 /**
  * @since 5.2
+ * @deprecated Use
+ * {@link org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter}
+ * instead
  */
-public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
-
-	public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/saml2/sso/{registrationId}";
-
-	private final AuthenticationConverter authenticationConverter;
-
-	private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();
+@Deprecated
+public class Saml2WebSsoAuthenticationFilter
+		extends org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter {
 
-	/**
-	 * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter that is
-	 * configured to use the {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL
-	 * @param relyingPartyRegistrationRepository - repository of configured SAML 2
-	 * entities. Required.
-	 */
 	public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
-		this(relyingPartyRegistrationRepository, DEFAULT_FILTER_PROCESSES_URI);
+		super(relyingPartyRegistrationRepository);
 	}
 
-	/**
-	 * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter
-	 * @param relyingPartyRegistrationRepository - repository of configured SAML 2
-	 * entities. Required.
-	 * @param filterProcessesUrl the processing URL, must contain a {registrationId}
-	 * variable. Required.
-	 */
 	public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,
 			String filterProcessesUrl) {
-		this(new Saml2AuthenticationTokenConverter(
-				(RelyingPartyRegistrationResolver) new DefaultRelyingPartyRegistrationResolver(
-						relyingPartyRegistrationRepository)),
-				filterProcessesUrl);
-		Assert.isTrue(filterProcessesUrl.contains("{registrationId}"),
-				"filterProcessesUrl must contain a {registrationId} match variable");
+		super(relyingPartyRegistrationRepository, filterProcessesUrl);
 	}
 
-	/**
-	 * Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
-	 * @param authenticationConverter the strategy for converting an
-	 * {@link HttpServletRequest} into an {@link Authentication}
-	 * @param filterProcessesUrl the processing URL
-	 * @since 5.4
-	 */
 	public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter, String filterProcessesUrl) {
-		super(filterProcessesUrl);
-		Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
-		Assert.hasText(filterProcessesUrl, "filterProcessesUrl must contain a URL pattern");
-		this.authenticationConverter = authenticationConverter;
-		setAllowSessionCreation(true);
-		setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
-	}
-
-	@Override
-	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
-		return super.requiresAuthentication(request, response);
-	}
-
-	@Override
-	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
-			throws AuthenticationException {
-		Authentication authentication = this.authenticationConverter.convert(request);
-		if (authentication == null) {
-			Saml2Error saml2Error = new Saml2Error(Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND,
-					"No relying party registration found");
-			throw new Saml2AuthenticationException(saml2Error);
-		}
-		setDetails(request, authentication);
-		this.authenticationRequestRepository.removeAuthenticationRequest(request, response);
-		return getAuthenticationManager().authenticate(authentication);
-	}
-
-	/**
-	 * Use the given {@link Saml2AuthenticationRequestRepository} to remove the saved
-	 * authentication request. If the {@link #authenticationConverter} is of the type
-	 * {@link Saml2AuthenticationTokenConverter}, the
-	 * {@link Saml2AuthenticationRequestRepository} will also be set into the
-	 * {@link #authenticationConverter}.
-	 * @param authenticationRequestRepository the
-	 * {@link Saml2AuthenticationRequestRepository} to use
-	 * @since 5.6
-	 */
-	public void setAuthenticationRequestRepository(
-			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
-		Assert.notNull(authenticationRequestRepository, "authenticationRequestRepository cannot be null");
-		this.authenticationRequestRepository = authenticationRequestRepository;
-		setAuthenticationRequestRepositoryIntoAuthenticationConverter(authenticationRequestRepository);
-	}
-
-	private void setAuthenticationRequestRepositoryIntoAuthenticationConverter(
-			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
-		if (this.authenticationConverter instanceof Saml2AuthenticationTokenConverter) {
-			Saml2AuthenticationTokenConverter authenticationTokenConverter = (Saml2AuthenticationTokenConverter) this.authenticationConverter;
-			authenticationTokenConverter.setAuthenticationRequestRepository(authenticationRequestRepository);
-		}
-	}
-
-	private void setDetails(HttpServletRequest request, Authentication authentication) {
-		if (AbstractAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
-			Object details = this.authenticationDetailsSource.buildDetails(request);
-			((AbstractAuthenticationToken) authentication).setDetails(details);
-		}
+		super(authenticationConverter, filterProcessesUrl);
 	}
 
 }

+ 9 - 253
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilter.java

@@ -16,43 +16,11 @@
 
 package org.springframework.security.saml2.provider.service.servlet.filter;
 
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.opensaml.core.Version;
-
-import org.springframework.http.MediaType;
-import org.springframework.security.saml2.core.Saml2ParameterNames;
-import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
-import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext;
 import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory;
-import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
-import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
-import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
-import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.DefaultSaml2AuthenticationRequestContextResolver;
-import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
-import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
 import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
-import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
 import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
-import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
-import org.springframework.security.web.util.matcher.RequestMatcher;
-import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.StringUtils;
-import org.springframework.web.filter.OncePerRequestFilter;
-import org.springframework.web.util.HtmlUtils;
-import org.springframework.web.util.UriComponentsBuilder;
-import org.springframework.web.util.UriUtils;
 
 /**
  * This {@code Filter} formulates a
@@ -76,239 +44,27 @@ import org.springframework.web.util.UriUtils;
  * @author Filip Hanik
  * @author Josh Cummings
  * @since 5.2
+ * @deprecated Use
+ * {@link org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter}
+ * instead
  */
-public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter {
-
-	private final Saml2AuthenticationRequestResolver authenticationRequestResolver;
+@Deprecated
+public class Saml2WebSsoAuthenticationRequestFilter
+		extends org.springframework.security.saml2.provider.service.web.Saml2WebSsoAuthenticationRequestFilter {
 
-	private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();
-
-	/**
-	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
-	 * parameters
-	 * @param relyingPartyRegistrationRepository a repository for relying party
-	 * configurations
-	 * @deprecated use the constructor that takes a
-	 * {@link Saml2AuthenticationRequestFactory}
-	 */
-	@Deprecated
 	public Saml2WebSsoAuthenticationRequestFilter(
 			RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
-		this(new DefaultSaml2AuthenticationRequestContextResolver(
-				(RelyingPartyRegistrationResolver) new DefaultRelyingPartyRegistrationResolver(
-						relyingPartyRegistrationRepository)),
-				requestFactory());
-	}
-
-	private static Saml2AuthenticationRequestFactory requestFactory() {
-		String opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory";
-		if (Version.getVersion().startsWith("4")) {
-			opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory";
-		}
-		try {
-			return (Saml2AuthenticationRequestFactory) ClassUtils.forName(opensamlClassName, null)
-					.getDeclaredConstructor().newInstance();
-		}
-		catch (Exception ex) {
-			throw new IllegalStateException(ex);
-		}
+		super(relyingPartyRegistrationRepository);
 	}
 
-	/**
-	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
-	 * parameters
-	 * @param authenticationRequestContextResolver a strategy for formulating a
-	 * {@link Saml2AuthenticationRequestContext}
-	 * @param authenticationRequestFactory strategy for formulating a
-	 * &lt;saml2:AuthnRequest&gt;
-	 * @since 5.4
-	 */
 	public Saml2WebSsoAuthenticationRequestFilter(
 			Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver,
 			Saml2AuthenticationRequestFactory authenticationRequestFactory) {
-		this(new FactorySaml2AuthenticationRequestResolver(authenticationRequestContextResolver,
-				authenticationRequestFactory));
+		super(authenticationRequestContextResolver, authenticationRequestFactory);
 	}
 
-	/**
-	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the strategy for
-	 * resolving the {@code AuthnRequest}
-	 * @param authenticationRequestResolver the strategy for resolving the
-	 * {@code AuthnRequest}
-	 * @since 5.7
-	 */
 	public Saml2WebSsoAuthenticationRequestFilter(Saml2AuthenticationRequestResolver authenticationRequestResolver) {
-		Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
-		this.authenticationRequestResolver = authenticationRequestResolver;
-	}
-
-	/**
-	 * Use the given {@link Saml2AuthenticationRequestFactory} for formulating the SAML
-	 * 2.0 AuthnRequest
-	 * @param authenticationRequestFactory the {@link Saml2AuthenticationRequestFactory}
-	 * to use
-	 * @deprecated use the constructor instead
-	 */
-	@Deprecated
-	public void setAuthenticationRequestFactory(Saml2AuthenticationRequestFactory authenticationRequestFactory) {
-		Assert.notNull(authenticationRequestFactory, "authenticationRequestFactory cannot be null");
-		Assert.isInstanceOf(FactorySaml2AuthenticationRequestResolver.class, this.authenticationRequestResolver,
-				"You cannot supply both a Saml2AuthenticationRequestResolver and a Saml2AuthenticationRequestFactory");
-		((FactorySaml2AuthenticationRequestResolver) this.authenticationRequestResolver).authenticationRequestFactory = authenticationRequestFactory;
-	}
-
-	/**
-	 * Use the given {@link RequestMatcher} that activates this filter for a given request
-	 * @param redirectMatcher the {@link RequestMatcher} to use
-	 * @deprecated Configure the request matcher in an implementation of
-	 * {@link Saml2AuthenticationRequestResolver} instead
-	 */
-	@Deprecated
-	public void setRedirectMatcher(RequestMatcher redirectMatcher) {
-		Assert.notNull(redirectMatcher, "redirectMatcher cannot be null");
-		Assert.isInstanceOf(FactorySaml2AuthenticationRequestResolver.class, this.authenticationRequestResolver,
-				"You cannot supply a Saml2AuthenticationRequestResolver and a redirect matcher");
-		((FactorySaml2AuthenticationRequestResolver) this.authenticationRequestResolver).redirectMatcher = redirectMatcher;
-	}
-
-	/**
-	 * Use the given {@link Saml2AuthenticationRequestRepository} to save the
-	 * authentication request
-	 * @param authenticationRequestRepository the
-	 * {@link Saml2AuthenticationRequestRepository} to use
-	 * @since 5.6
-	 */
-	public void setAuthenticationRequestRepository(
-			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
-		Assert.notNull(authenticationRequestRepository, "authenticationRequestRepository cannot be null");
-		this.authenticationRequestRepository = authenticationRequestRepository;
-	}
-
-	@Override
-	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
-			throws ServletException, IOException {
-		AbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestResolver.resolve(request);
-		if (authenticationRequest == null) {
-			filterChain.doFilter(request, response);
-			return;
-		}
-		if (authenticationRequest instanceof Saml2RedirectAuthenticationRequest) {
-			sendRedirect(request, response, (Saml2RedirectAuthenticationRequest) authenticationRequest);
-		}
-		else {
-			sendPost(request, response, (Saml2PostAuthenticationRequest) authenticationRequest);
-		}
-	}
-
-	private void sendRedirect(HttpServletRequest request, HttpServletResponse response,
-			Saml2RedirectAuthenticationRequest authenticationRequest) throws IOException {
-		this.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);
-		UriComponentsBuilder uriBuilder = UriComponentsBuilder
-				.fromUriString(authenticationRequest.getAuthenticationRequestUri());
-		addParameter(Saml2ParameterNames.SAML_REQUEST, authenticationRequest.getSamlRequest(), uriBuilder);
-		addParameter(Saml2ParameterNames.RELAY_STATE, authenticationRequest.getRelayState(), uriBuilder);
-		addParameter(Saml2ParameterNames.SIG_ALG, authenticationRequest.getSigAlg(), uriBuilder);
-		addParameter(Saml2ParameterNames.SIGNATURE, authenticationRequest.getSignature(), uriBuilder);
-		String redirectUrl = uriBuilder.build(true).toUriString();
-		response.sendRedirect(redirectUrl);
-	}
-
-	private void addParameter(String name, String value, UriComponentsBuilder builder) {
-		Assert.hasText(name, "name cannot be empty or null");
-		if (StringUtils.hasText(value)) {
-			builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1),
-					UriUtils.encode(value, StandardCharsets.ISO_8859_1));
-		}
-	}
-
-	private void sendPost(HttpServletRequest request, HttpServletResponse response,
-			Saml2PostAuthenticationRequest authenticationRequest) throws IOException {
-		this.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);
-		String html = createSamlPostRequestFormData(authenticationRequest);
-		response.setContentType(MediaType.TEXT_HTML_VALUE);
-		response.getWriter().write(html);
-	}
-
-	private String createSamlPostRequestFormData(Saml2PostAuthenticationRequest authenticationRequest) {
-		String authenticationRequestUri = authenticationRequest.getAuthenticationRequestUri();
-		String relayState = authenticationRequest.getRelayState();
-		String samlRequest = authenticationRequest.getSamlRequest();
-		StringBuilder html = new StringBuilder();
-		html.append("<!DOCTYPE html>\n");
-		html.append("<html>\n").append("    <head>\n");
-		html.append("        <meta http-equiv=\"Content-Security-Policy\" ")
-				.append("content=\"script-src 'sha256-t+jmhLjs1ocvgaHBJsFcgznRk68d37TLtbI3NE9h7EU='\">\n");
-		html.append("        <meta charset=\"utf-8\" />\n");
-		html.append("    </head>\n");
-		html.append("    <body>\n");
-		html.append("        <noscript>\n");
-		html.append("            <p>\n");
-		html.append("                <strong>Note:</strong> Since your browser does not support JavaScript,\n");
-		html.append("                you must press the Continue button once to proceed.\n");
-		html.append("            </p>\n");
-		html.append("        </noscript>\n");
-		html.append("        \n");
-		html.append("        <form action=\"");
-		html.append(authenticationRequestUri);
-		html.append("\" method=\"post\">\n");
-		html.append("            <div>\n");
-		html.append("                <input type=\"hidden\" name=\"SAMLRequest\" value=\"");
-		html.append(HtmlUtils.htmlEscape(samlRequest));
-		html.append("\"/>\n");
-		if (StringUtils.hasText(relayState)) {
-			html.append("                <input type=\"hidden\" name=\"RelayState\" value=\"");
-			html.append(HtmlUtils.htmlEscape(relayState));
-			html.append("\"/>\n");
-		}
-		html.append("            </div>\n");
-		html.append("            <noscript>\n");
-		html.append("                <div>\n");
-		html.append("                    <input type=\"submit\" value=\"Continue\"/>\n");
-		html.append("                </div>\n");
-		html.append("            </noscript>\n");
-		html.append("        </form>\n");
-		html.append("        \n");
-		html.append("        <script>window.onload = () => document.forms[0].submit();</script>\n");
-		html.append("    </body>\n");
-		html.append("</html>");
-		return html.toString();
-	}
-
-	private static class FactorySaml2AuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {
-
-		private final Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver;
-
-		private RequestMatcher redirectMatcher = new AntPathRequestMatcher("/saml2/authenticate/{registrationId}");
-
-		private Saml2AuthenticationRequestFactory authenticationRequestFactory;
-
-		FactorySaml2AuthenticationRequestResolver(
-				Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver,
-				Saml2AuthenticationRequestFactory authenticationRequestFactory) {
-			Assert.notNull(authenticationRequestContextResolver, "authenticationRequestContextResolver cannot be null");
-			Assert.notNull(authenticationRequestFactory, "authenticationRequestFactory cannot be null");
-			this.authenticationRequestContextResolver = authenticationRequestContextResolver;
-			this.authenticationRequestFactory = authenticationRequestFactory;
-		}
-
-		@Override
-		public AbstractSaml2AuthenticationRequest resolve(HttpServletRequest request) {
-			MatchResult matcher = this.redirectMatcher.matcher(request);
-			if (!matcher.isMatch()) {
-				return null;
-			}
-			Saml2AuthenticationRequestContext context = this.authenticationRequestContextResolver.resolve(request);
-			if (context == null) {
-				return null;
-			}
-			Saml2MessageBinding binding = context.getRelyingPartyRegistration().getAssertingPartyDetails()
-					.getSingleSignOnServiceBinding();
-			if (binding == Saml2MessageBinding.REDIRECT) {
-				return this.authenticationRequestFactory.createRedirectAuthenticationRequest(context);
-			}
-			return this.authenticationRequestFactory.createPostAuthenticationRequest(context);
-		}
-
+		super(authenticationRequestResolver);
 	}
 
 }

+ 308 - 0
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilter.java

@@ -0,0 +1,308 @@
+/*
+ * Copyright 2002-2022 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.saml2.provider.service.web;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.opensaml.core.Version;
+
+import org.springframework.http.MediaType;
+import org.springframework.security.saml2.core.Saml2ParameterNames;
+import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestContext;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationRequestFactory;
+import org.springframework.security.saml2.provider.service.authentication.Saml2PostAuthenticationRequest;
+import org.springframework.security.saml2.provider.service.authentication.Saml2RedirectAuthenticationRequest;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher.MatchResult;
+import org.springframework.util.Assert;
+import org.springframework.util.ClassUtils;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.HtmlUtils;
+import org.springframework.web.util.UriComponentsBuilder;
+import org.springframework.web.util.UriUtils;
+
+/**
+ * This {@code Filter} formulates a
+ * <a href="https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">SAML 2.0
+ * AuthnRequest</a> (line 1968) and redirects to a configured asserting party.
+ *
+ * <p>
+ * It supports the <a href=
+ * "https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-Redirect</a>
+ * (line 520) and <a href=
+ * "https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf">HTTP-POST</a>
+ * (line 753) bindings.
+ *
+ * <p>
+ * By default, this {@code Filter} responds to authentication requests at the {@code URI}
+ * {@code /saml2/authenticate/{registrationId}}. The {@code URI} template variable
+ * {@code {registrationId}} represents the
+ * {@link RelyingPartyRegistration#getRegistrationId() registration identifier} of the
+ * relying party that is used for initiating the authentication request.
+ *
+ * @author Filip Hanik
+ * @author Josh Cummings
+ * @since 5.2
+ */
+public class Saml2WebSsoAuthenticationRequestFilter extends OncePerRequestFilter {
+
+	private final Saml2AuthenticationRequestResolver authenticationRequestResolver;
+
+	private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();
+
+	/**
+	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
+	 * parameters
+	 * @param relyingPartyRegistrationRepository a repository for relying party
+	 * configurations
+	 * @deprecated use the constructor that takes a
+	 * {@link Saml2AuthenticationRequestFactory}
+	 */
+	@Deprecated
+	public Saml2WebSsoAuthenticationRequestFilter(
+			RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
+		this(new DefaultSaml2AuthenticationRequestContextResolver(
+				(RelyingPartyRegistrationResolver) new DefaultRelyingPartyRegistrationResolver(
+						relyingPartyRegistrationRepository)),
+				requestFactory());
+	}
+
+	private static Saml2AuthenticationRequestFactory requestFactory() {
+		String opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationRequestFactory";
+		if (Version.getVersion().startsWith("4")) {
+			opensamlClassName = "org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationRequestFactory";
+		}
+		try {
+			return (Saml2AuthenticationRequestFactory) ClassUtils.forName(opensamlClassName, null)
+					.getDeclaredConstructor().newInstance();
+		}
+		catch (Exception ex) {
+			throw new IllegalStateException(ex);
+		}
+	}
+
+	/**
+	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the provided
+	 * parameters
+	 * @param authenticationRequestContextResolver a strategy for formulating a
+	 * {@link Saml2AuthenticationRequestContext}
+	 * @param authenticationRequestFactory strategy for formulating a
+	 * &lt;saml2:AuthnRequest&gt;
+	 * @since 5.4
+	 */
+	public Saml2WebSsoAuthenticationRequestFilter(
+			Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver,
+			Saml2AuthenticationRequestFactory authenticationRequestFactory) {
+		this(new FactorySaml2AuthenticationRequestResolver(authenticationRequestContextResolver,
+				authenticationRequestFactory));
+	}
+
+	/**
+	 * Construct a {@link Saml2WebSsoAuthenticationRequestFilter} with the strategy for
+	 * resolving the {@code AuthnRequest}
+	 * @param authenticationRequestResolver the strategy for resolving the
+	 * {@code AuthnRequest}
+	 * @since 5.7
+	 */
+	public Saml2WebSsoAuthenticationRequestFilter(Saml2AuthenticationRequestResolver authenticationRequestResolver) {
+		Assert.notNull(authenticationRequestResolver, "authenticationRequestResolver cannot be null");
+		this.authenticationRequestResolver = authenticationRequestResolver;
+	}
+
+	/**
+	 * Use the given {@link Saml2AuthenticationRequestFactory} for formulating the SAML
+	 * 2.0 AuthnRequest
+	 * @param authenticationRequestFactory the {@link Saml2AuthenticationRequestFactory}
+	 * to use
+	 * @deprecated use the constructor instead
+	 */
+	@Deprecated
+	public void setAuthenticationRequestFactory(Saml2AuthenticationRequestFactory authenticationRequestFactory) {
+		Assert.notNull(authenticationRequestFactory, "authenticationRequestFactory cannot be null");
+		Assert.isInstanceOf(FactorySaml2AuthenticationRequestResolver.class, this.authenticationRequestResolver,
+				"You cannot supply both a Saml2AuthenticationRequestResolver and a Saml2AuthenticationRequestFactory");
+		((FactorySaml2AuthenticationRequestResolver) this.authenticationRequestResolver).authenticationRequestFactory = authenticationRequestFactory;
+	}
+
+	/**
+	 * Use the given {@link RequestMatcher} that activates this filter for a given request
+	 * @param redirectMatcher the {@link RequestMatcher} to use
+	 * @deprecated Configure the request matcher in an implementation of
+	 * {@link Saml2AuthenticationRequestResolver} instead
+	 */
+	@Deprecated
+	public void setRedirectMatcher(RequestMatcher redirectMatcher) {
+		Assert.notNull(redirectMatcher, "redirectMatcher cannot be null");
+		Assert.isInstanceOf(FactorySaml2AuthenticationRequestResolver.class, this.authenticationRequestResolver,
+				"You cannot supply a Saml2AuthenticationRequestResolver and a redirect matcher");
+		((FactorySaml2AuthenticationRequestResolver) this.authenticationRequestResolver).redirectMatcher = redirectMatcher;
+	}
+
+	/**
+	 * Use the given {@link Saml2AuthenticationRequestRepository} to save the
+	 * authentication request
+	 * @param authenticationRequestRepository the
+	 * {@link Saml2AuthenticationRequestRepository} to use
+	 * @since 5.6
+	 */
+	public void setAuthenticationRequestRepository(
+			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
+		Assert.notNull(authenticationRequestRepository, "authenticationRequestRepository cannot be null");
+		this.authenticationRequestRepository = authenticationRequestRepository;
+	}
+
+	@Override
+	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
+			throws ServletException, IOException {
+		AbstractSaml2AuthenticationRequest authenticationRequest = this.authenticationRequestResolver.resolve(request);
+		if (authenticationRequest == null) {
+			filterChain.doFilter(request, response);
+			return;
+		}
+		if (authenticationRequest instanceof Saml2RedirectAuthenticationRequest) {
+			sendRedirect(request, response, (Saml2RedirectAuthenticationRequest) authenticationRequest);
+		}
+		else {
+			sendPost(request, response, (Saml2PostAuthenticationRequest) authenticationRequest);
+		}
+	}
+
+	private void sendRedirect(HttpServletRequest request, HttpServletResponse response,
+			Saml2RedirectAuthenticationRequest authenticationRequest) throws IOException {
+		this.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);
+		UriComponentsBuilder uriBuilder = UriComponentsBuilder
+				.fromUriString(authenticationRequest.getAuthenticationRequestUri());
+		addParameter(Saml2ParameterNames.SAML_REQUEST, authenticationRequest.getSamlRequest(), uriBuilder);
+		addParameter(Saml2ParameterNames.RELAY_STATE, authenticationRequest.getRelayState(), uriBuilder);
+		addParameter(Saml2ParameterNames.SIG_ALG, authenticationRequest.getSigAlg(), uriBuilder);
+		addParameter(Saml2ParameterNames.SIGNATURE, authenticationRequest.getSignature(), uriBuilder);
+		String redirectUrl = uriBuilder.build(true).toUriString();
+		response.sendRedirect(redirectUrl);
+	}
+
+	private void addParameter(String name, String value, UriComponentsBuilder builder) {
+		Assert.hasText(name, "name cannot be empty or null");
+		if (StringUtils.hasText(value)) {
+			builder.queryParam(UriUtils.encode(name, StandardCharsets.ISO_8859_1),
+					UriUtils.encode(value, StandardCharsets.ISO_8859_1));
+		}
+	}
+
+	private void sendPost(HttpServletRequest request, HttpServletResponse response,
+			Saml2PostAuthenticationRequest authenticationRequest) throws IOException {
+		this.authenticationRequestRepository.saveAuthenticationRequest(authenticationRequest, request, response);
+		String html = createSamlPostRequestFormData(authenticationRequest);
+		response.setContentType(MediaType.TEXT_HTML_VALUE);
+		response.getWriter().write(html);
+	}
+
+	private String createSamlPostRequestFormData(Saml2PostAuthenticationRequest authenticationRequest) {
+		String authenticationRequestUri = authenticationRequest.getAuthenticationRequestUri();
+		String relayState = authenticationRequest.getRelayState();
+		String samlRequest = authenticationRequest.getSamlRequest();
+		StringBuilder html = new StringBuilder();
+		html.append("<!DOCTYPE html>\n");
+		html.append("<html>\n").append("    <head>\n");
+		html.append("        <meta http-equiv=\"Content-Security-Policy\" ")
+				.append("content=\"script-src 'sha256-t+jmhLjs1ocvgaHBJsFcgznRk68d37TLtbI3NE9h7EU='\">\n");
+		html.append("        <meta charset=\"utf-8\" />\n");
+		html.append("    </head>\n");
+		html.append("    <body>\n");
+		html.append("        <noscript>\n");
+		html.append("            <p>\n");
+		html.append("                <strong>Note:</strong> Since your browser does not support JavaScript,\n");
+		html.append("                you must press the Continue button once to proceed.\n");
+		html.append("            </p>\n");
+		html.append("        </noscript>\n");
+		html.append("        \n");
+		html.append("        <form action=\"");
+		html.append(authenticationRequestUri);
+		html.append("\" method=\"post\">\n");
+		html.append("            <div>\n");
+		html.append("                <input type=\"hidden\" name=\"SAMLRequest\" value=\"");
+		html.append(HtmlUtils.htmlEscape(samlRequest));
+		html.append("\"/>\n");
+		if (StringUtils.hasText(relayState)) {
+			html.append("                <input type=\"hidden\" name=\"RelayState\" value=\"");
+			html.append(HtmlUtils.htmlEscape(relayState));
+			html.append("\"/>\n");
+		}
+		html.append("            </div>\n");
+		html.append("            <noscript>\n");
+		html.append("                <div>\n");
+		html.append("                    <input type=\"submit\" value=\"Continue\"/>\n");
+		html.append("                </div>\n");
+		html.append("            </noscript>\n");
+		html.append("        </form>\n");
+		html.append("        \n");
+		html.append("        <script>window.onload = () => document.forms[0].submit();</script>\n");
+		html.append("    </body>\n");
+		html.append("</html>");
+		return html.toString();
+	}
+
+	private static class FactorySaml2AuthenticationRequestResolver implements Saml2AuthenticationRequestResolver {
+
+		private final Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver;
+
+		private RequestMatcher redirectMatcher = new AntPathRequestMatcher("/saml2/authenticate/{registrationId}");
+
+		private Saml2AuthenticationRequestFactory authenticationRequestFactory;
+
+		FactorySaml2AuthenticationRequestResolver(
+				Saml2AuthenticationRequestContextResolver authenticationRequestContextResolver,
+				Saml2AuthenticationRequestFactory authenticationRequestFactory) {
+			Assert.notNull(authenticationRequestContextResolver, "authenticationRequestContextResolver cannot be null");
+			Assert.notNull(authenticationRequestFactory, "authenticationRequestFactory cannot be null");
+			this.authenticationRequestContextResolver = authenticationRequestContextResolver;
+			this.authenticationRequestFactory = authenticationRequestFactory;
+		}
+
+		@Override
+		public AbstractSaml2AuthenticationRequest resolve(HttpServletRequest request) {
+			MatchResult matcher = this.redirectMatcher.matcher(request);
+			if (!matcher.isMatch()) {
+				return null;
+			}
+			Saml2AuthenticationRequestContext context = this.authenticationRequestContextResolver.resolve(request);
+			if (context == null) {
+				return null;
+			}
+			Saml2MessageBinding binding = context.getRelyingPartyRegistration().getAssertingPartyDetails()
+					.getSingleSignOnServiceBinding();
+			if (binding == Saml2MessageBinding.REDIRECT) {
+				return this.authenticationRequestFactory.createRedirectAuthenticationRequest(context);
+			}
+			return this.authenticationRequestFactory.createPostAuthenticationRequest(context);
+		}
+
+	}
+
+}

+ 145 - 0
saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilter.java

@@ -0,0 +1,145 @@
+/*
+ * Copyright 2002-2021 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.security.saml2.provider.service.web.authentication;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.saml2.core.Saml2Error;
+import org.springframework.security.saml2.core.Saml2ErrorCodes;
+import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
+import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException;
+import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
+import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
+import org.springframework.security.saml2.provider.service.web.HttpSessionSaml2AuthenticationRequestRepository;
+import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
+import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
+import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter;
+import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
+import org.springframework.security.web.authentication.AuthenticationConverter;
+import org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy;
+import org.springframework.util.Assert;
+
+/**
+ * @since 5.2
+ */
+public class Saml2WebSsoAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
+
+	public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/saml2/sso/{registrationId}";
+
+	private final AuthenticationConverter authenticationConverter;
+
+	private Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository = new HttpSessionSaml2AuthenticationRequestRepository();
+
+	/**
+	 * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter that is
+	 * configured to use the {@link #DEFAULT_FILTER_PROCESSES_URI} processing URL
+	 * @param relyingPartyRegistrationRepository - repository of configured SAML 2
+	 * entities. Required.
+	 */
+	public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository) {
+		this(relyingPartyRegistrationRepository, DEFAULT_FILTER_PROCESSES_URI);
+	}
+
+	/**
+	 * Creates a {@code Saml2WebSsoAuthenticationFilter} authentication filter
+	 * @param relyingPartyRegistrationRepository - repository of configured SAML 2
+	 * entities. Required.
+	 * @param filterProcessesUrl the processing URL, must contain a {registrationId}
+	 * variable. Required.
+	 */
+	public Saml2WebSsoAuthenticationFilter(RelyingPartyRegistrationRepository relyingPartyRegistrationRepository,
+			String filterProcessesUrl) {
+		this(new Saml2AuthenticationTokenConverter(
+				(RelyingPartyRegistrationResolver) new DefaultRelyingPartyRegistrationResolver(
+						relyingPartyRegistrationRepository)),
+				filterProcessesUrl);
+		Assert.isTrue(filterProcessesUrl.contains("{registrationId}"),
+				"filterProcessesUrl must contain a {registrationId} match variable");
+	}
+
+	/**
+	 * Creates a {@link Saml2WebSsoAuthenticationFilter} given the provided parameters
+	 * @param authenticationConverter the strategy for converting an
+	 * {@link HttpServletRequest} into an {@link Authentication}
+	 * @param filterProcessesUrl the processing URL
+	 * @since 5.4
+	 */
+	public Saml2WebSsoAuthenticationFilter(AuthenticationConverter authenticationConverter, String filterProcessesUrl) {
+		super(filterProcessesUrl);
+		Assert.notNull(authenticationConverter, "authenticationConverter cannot be null");
+		Assert.hasText(filterProcessesUrl, "filterProcessesUrl must contain a URL pattern");
+		this.authenticationConverter = authenticationConverter;
+		setAllowSessionCreation(true);
+		setSessionAuthenticationStrategy(new ChangeSessionIdAuthenticationStrategy());
+	}
+
+	@Override
+	protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
+		return super.requiresAuthentication(request, response);
+	}
+
+	@Override
+	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
+			throws AuthenticationException {
+		Authentication authentication = this.authenticationConverter.convert(request);
+		if (authentication == null) {
+			Saml2Error saml2Error = new Saml2Error(Saml2ErrorCodes.RELYING_PARTY_REGISTRATION_NOT_FOUND,
+					"No relying party registration found");
+			throw new Saml2AuthenticationException(saml2Error);
+		}
+		setDetails(request, authentication);
+		this.authenticationRequestRepository.removeAuthenticationRequest(request, response);
+		return getAuthenticationManager().authenticate(authentication);
+	}
+
+	/**
+	 * Use the given {@link Saml2AuthenticationRequestRepository} to remove the saved
+	 * authentication request. If the {@link #authenticationConverter} is of the type
+	 * {@link Saml2AuthenticationTokenConverter}, the
+	 * {@link Saml2AuthenticationRequestRepository} will also be set into the
+	 * {@link #authenticationConverter}.
+	 * @param authenticationRequestRepository the
+	 * {@link Saml2AuthenticationRequestRepository} to use
+	 * @since 5.6
+	 */
+	public void setAuthenticationRequestRepository(
+			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
+		Assert.notNull(authenticationRequestRepository, "authenticationRequestRepository cannot be null");
+		this.authenticationRequestRepository = authenticationRequestRepository;
+		setAuthenticationRequestRepositoryIntoAuthenticationConverter(authenticationRequestRepository);
+	}
+
+	private void setAuthenticationRequestRepositoryIntoAuthenticationConverter(
+			Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> authenticationRequestRepository) {
+		if (this.authenticationConverter instanceof Saml2AuthenticationTokenConverter) {
+			Saml2AuthenticationTokenConverter authenticationTokenConverter = (Saml2AuthenticationTokenConverter) this.authenticationConverter;
+			authenticationTokenConverter.setAuthenticationRequestRepository(authenticationRequestRepository);
+		}
+	}
+
+	private void setDetails(HttpServletRequest request, Authentication authentication) {
+		if (AbstractAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
+			Object details = this.authenticationDetailsSource.buildDetails(request);
+			((AbstractAuthenticationToken) authentication).setDetails(details);
+		}
+	}
+
+}

+ 1 - 1
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/RelyingPartyRegistrationTests.java

@@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.security.saml2.core.Saml2X509Credential;
 import org.springframework.security.saml2.core.TestSaml2X509Credentials;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 
 import static org.assertj.core.api.Assertions.assertThat;
 

+ 1 - 1
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/registration/TestRelyingPartyRegistrations.java

@@ -18,7 +18,7 @@ package org.springframework.security.saml2.provider.service.registration;
 
 import org.springframework.security.saml2.credentials.Saml2X509Credential;
 import org.springframework.security.saml2.credentials.TestSaml2X509Credentials;
-import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter;
+import org.springframework.security.saml2.provider.service.web.authentication.Saml2WebSsoAuthenticationFilter;
 
 /**
  * Preconfigured test data for {@link RelyingPartyRegistration} objects

+ 1 - 6
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationRequestFilterTests.java → saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/Saml2WebSsoAuthenticationRequestFilterTests.java

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.springframework.security.saml2.provider.service.servlet.filter;
+package org.springframework.security.saml2.provider.service.web;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -41,11 +41,6 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
 import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository;
 import org.springframework.security.saml2.provider.service.registration.Saml2MessageBinding;
 import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations;
-import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.DefaultSaml2AuthenticationRequestContextResolver;
-import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver;
-import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestContextResolver;
-import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
 import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;

+ 1 - 1
saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/servlet/filter/Saml2WebSsoAuthenticationFilterTests.java → saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/Saml2WebSsoAuthenticationFilterTests.java

@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package org.springframework.security.saml2.provider.service.servlet.filter;
+package org.springframework.security.saml2.provider.service.web.authentication;
 
 import javax.servlet.http.HttpServletResponse;