فهرست منبع

Allow configuring request paths for oauth2 filters

Fixes gh-4473
Joe Grandja 8 سال پیش
والد
کامیت
93c2b2533e

+ 24 - 12
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/AuthorizationCodeAuthenticationFilterConfigurer.java

@@ -38,6 +38,7 @@ import org.springframework.security.oauth2.core.provider.DefaultProviderMetadata
 import org.springframework.security.oauth2.core.provider.ProviderMetadata;
 import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
 import org.springframework.util.Assert;
 import org.springframework.util.StringUtils;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -51,9 +52,10 @@ import java.util.Map;
 /**
  * @author Joe Grandja
  */
-final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>> extends
-		AbstractAuthenticationFilterConfigurer<H, AuthorizationCodeAuthenticationFilterConfigurer<H>, AuthorizationCodeAuthenticationProcessingFilter> {
+final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecurityBuilder<H>, R extends RequestMatcher & RequestVariablesExtractor> extends
+		AbstractAuthenticationFilterConfigurer<H, AuthorizationCodeAuthenticationFilterConfigurer<H, R>, AuthorizationCodeAuthenticationProcessingFilter> {
 
+	private R authorizationResponseMatcher;
 	private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger;
 	private OAuth2UserService userInfoService;
 	private Map<URI, Class<? extends OAuth2User>> customUserTypes = new HashMap<>();
@@ -64,14 +66,13 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
 		super(new AuthorizationCodeAuthenticationProcessingFilter(), null);
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
-		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
-		Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
-		this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> authorizationResponseMatcher(R authorizationResponseMatcher) {
+		Assert.notNull(authorizationResponseMatcher, "authorizationResponseMatcher cannot be null");
+		this.authorizationResponseMatcher = authorizationResponseMatcher;
 		return this;
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> authorizationCodeTokenExchanger(
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> authorizationCodeTokenExchanger(
 			AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
 
 		Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
@@ -79,32 +80,39 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
 		return this;
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> userInfoService(OAuth2UserService userInfoService) {
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> userInfoService(OAuth2UserService userInfoService) {
 		Assert.notNull(userInfoService, "userInfoService cannot be null");
 		this.userInfoService = userInfoService;
 		return this;
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
 		Assert.notNull(customUserType, "customUserType cannot be null");
 		Assert.notNull(userInfoUri, "userInfoUri cannot be null");
 		this.customUserTypes.put(userInfoUri, customUserType);
 		return this;
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
 		Assert.hasText(userNameAttributeName, "userNameAttributeName cannot be empty");
 		Assert.notNull(userInfoUri, "userInfoUri cannot be null");
 		this.userNameAttributeNames.put(userInfoUri, userNameAttributeName);
 		return this;
 	}
 
-	AuthorizationCodeAuthenticationFilterConfigurer<H> userAuthoritiesMapper(GrantedAuthoritiesMapper userAuthoritiesMapper) {
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> userAuthoritiesMapper(GrantedAuthoritiesMapper userAuthoritiesMapper) {
 		Assert.notNull(userAuthoritiesMapper, "userAuthoritiesMapper cannot be null");
 		this.userAuthoritiesMapper = userAuthoritiesMapper;
 		return this;
 	}
 
+	AuthorizationCodeAuthenticationFilterConfigurer<H, R> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
+		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
+		Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
+		this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
+		return this;
+	}
+
 	String getLoginUrl() {
 		return super.getLoginPage();
 	}
@@ -128,13 +136,17 @@ final class AuthorizationCodeAuthenticationFilterConfigurer<H extends HttpSecuri
 	@Override
 	public void configure(H http) throws Exception {
 		AuthorizationCodeAuthenticationProcessingFilter authFilter = this.getAuthenticationFilter();
+		if (this.authorizationResponseMatcher != null) {
+			authFilter.setAuthorizationResponseMatcher(this.authorizationResponseMatcher);
+		}
 		authFilter.setClientRegistrationRepository(OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder()));
 		super.configure(http);
 	}
 
 	@Override
 	protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
-		return this.getAuthenticationFilter().getAuthorizeRequestMatcher();
+		return (this.authorizationResponseMatcher != null ?
+			this.authorizationResponseMatcher : this.getAuthenticationFilter().getAuthorizationResponseMatcher());
 	}
 
 	private AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> getAuthorizationCodeTokenExchanger(H http) {

+ 25 - 10
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/AuthorizationCodeRequestRedirectFilterConfigurer.java

@@ -21,34 +21,49 @@ import org.springframework.security.oauth2.client.authentication.AuthorizationCo
 import org.springframework.security.oauth2.client.authentication.AuthorizationRequestUriBuilder;
 import org.springframework.security.oauth2.client.authentication.DefaultAuthorizationRequestUriBuilder;
 import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
 import org.springframework.util.Assert;
 
 /**
  * @author Joe Grandja
  */
-final class AuthorizationCodeRequestRedirectFilterConfigurer<B extends HttpSecurityBuilder<B>> extends
-		AbstractHttpConfigurer<AuthorizationCodeRequestRedirectFilterConfigurer<B>, B> {
+final class AuthorizationCodeRequestRedirectFilterConfigurer<H extends HttpSecurityBuilder<H>, R extends RequestMatcher & RequestVariablesExtractor> extends
+		AbstractHttpConfigurer<AuthorizationCodeRequestRedirectFilterConfigurer<H, R>, H> {
 
+	private R authorizationRequestMatcher;
 	private AuthorizationRequestUriBuilder authorizationRequestBuilder;
 
-	AuthorizationCodeRequestRedirectFilterConfigurer<B> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
-		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
-		Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
-		this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
+	AuthorizationCodeRequestRedirectFilterConfigurer<H, R> authorizationRequestMatcher(R authorizationRequestMatcher) {
+		Assert.notNull(authorizationRequestMatcher, "authorizationRequestMatcher cannot be null");
+		this.authorizationRequestMatcher = authorizationRequestMatcher;
 		return this;
 	}
 
-	AuthorizationCodeRequestRedirectFilterConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
+	AuthorizationCodeRequestRedirectFilterConfigurer<H, R> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
 		Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
 		this.authorizationRequestBuilder = authorizationRequestBuilder;
 		return this;
 	}
 
+	AuthorizationCodeRequestRedirectFilterConfigurer<H, R> clientRegistrationRepository(ClientRegistrationRepository clientRegistrationRepository) {
+		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
+		Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
+		this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
+		return this;
+	}
+
+	R getAuthorizationRequestMatcher() {
+		return this.authorizationRequestMatcher;
+	}
+
 	@Override
-	public void configure(B http) throws Exception {
+	public void configure(H http) throws Exception {
 		AuthorizationCodeRequestRedirectFilter filter = new AuthorizationCodeRequestRedirectFilter(
-				OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder()),
-				this.getAuthorizationRequestBuilder());
+				OAuth2LoginConfigurer.getClientRegistrationRepository(this.getBuilder()), this.getAuthorizationRequestBuilder());
+		if (this.authorizationRequestMatcher != null) {
+			filter.setAuthorizationRequestMatcher(this.authorizationRequestMatcher);
+		}
 		http.addFilter(this.postProcess(filter));
 	}
 

+ 93 - 28
config/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/client/OAuth2LoginConfigurer.java

@@ -29,6 +29,9 @@ import org.springframework.security.oauth2.client.registration.InMemoryClientReg
 import org.springframework.security.oauth2.client.user.OAuth2UserService;
 import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
 import org.springframework.util.Assert;
 import org.springframework.util.CollectionUtils;
 
@@ -37,41 +40,41 @@ import java.util.Arrays;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import static org.springframework.security.oauth2.client.authentication.AuthorizationCodeRequestRedirectFilter.CLIENT_ALIAS_URI_VARIABLE_NAME;
+
 /**
  * @author Joe Grandja
  */
-public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> extends
-		AbstractHttpConfigurer<OAuth2LoginConfigurer<B>, B> {
+public final class OAuth2LoginConfigurer<H extends HttpSecurityBuilder<H>> extends
+		AbstractHttpConfigurer<OAuth2LoginConfigurer<H>, H> {
 
-	private final AuthorizationCodeRequestRedirectFilterConfigurer<B> authorizationCodeRequestRedirectFilterConfigurer;
-	private final AuthorizationCodeAuthenticationFilterConfigurer<B> authorizationCodeAuthenticationFilterConfigurer;
+	private final AuthorizationCodeRequestRedirectFilterConfigurer authorizationCodeRequestRedirectFilterConfigurer;
+	private final AuthorizationCodeAuthenticationFilterConfigurer authorizationCodeAuthenticationFilterConfigurer;
+	private final AuthorizationEndpointConfig authorizationEndpoint;
+	private final RedirectionEndpointConfig redirectionEndpoint;
 	private final UserInfoEndpointConfig userInfoEndpointConfig;
 
 	public OAuth2LoginConfigurer() {
 		this.authorizationCodeRequestRedirectFilterConfigurer = new AuthorizationCodeRequestRedirectFilterConfigurer<>();
 		this.authorizationCodeAuthenticationFilterConfigurer = new AuthorizationCodeAuthenticationFilterConfigurer<>();
+		this.authorizationEndpoint = new AuthorizationEndpointConfig();
+		this.redirectionEndpoint = new RedirectionEndpointConfig();
 		this.userInfoEndpointConfig = new UserInfoEndpointConfig();
 	}
 
-	public OAuth2LoginConfigurer<B> clients(ClientRegistration... clientRegistrations) {
+	public OAuth2LoginConfigurer<H> clients(ClientRegistration... clientRegistrations) {
 		Assert.notEmpty(clientRegistrations, "clientRegistrations cannot be empty");
-		return clients(new InMemoryClientRegistrationRepository(Arrays.asList(clientRegistrations)));
+		return this.clients(new InMemoryClientRegistrationRepository(Arrays.asList(clientRegistrations)));
 	}
 
-	public OAuth2LoginConfigurer<B> clients(ClientRegistrationRepository clientRegistrationRepository) {
+	public OAuth2LoginConfigurer<H> clients(ClientRegistrationRepository clientRegistrationRepository) {
 		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
 		Assert.notEmpty(clientRegistrationRepository.getRegistrations(), "clientRegistrationRepository cannot be empty");
 		this.getBuilder().setSharedObject(ClientRegistrationRepository.class, clientRegistrationRepository);
 		return this;
 	}
 
-	public OAuth2LoginConfigurer<B> authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
-		Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
-		this.authorizationCodeRequestRedirectFilterConfigurer.authorizationRequestBuilder(authorizationRequestBuilder);
-		return this;
-	}
-
-	public OAuth2LoginConfigurer<B> authorizationCodeTokenExchanger(
+	public OAuth2LoginConfigurer<H> authorizationCodeTokenExchanger(
 			AuthorizationGrantTokenExchanger<AuthorizationCodeAuthenticationToken> authorizationCodeTokenExchanger) {
 
 		Assert.notNull(authorizationCodeTokenExchanger, "authorizationCodeTokenExchanger cannot be null");
@@ -79,12 +82,58 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 		return this;
 	}
 
-	public OAuth2LoginConfigurer<B> userAuthoritiesMapper(GrantedAuthoritiesMapper userAuthoritiesMapper) {
+	public OAuth2LoginConfigurer<H> userAuthoritiesMapper(GrantedAuthoritiesMapper userAuthoritiesMapper) {
 		Assert.notNull(userAuthoritiesMapper, "userAuthoritiesMapper cannot be null");
 		this.authorizationCodeAuthenticationFilterConfigurer.userAuthoritiesMapper(userAuthoritiesMapper);
 		return this;
 	}
 
+	public AuthorizationEndpointConfig authorizationEndpoint() {
+		return this.authorizationEndpoint;
+	}
+
+	public class AuthorizationEndpointConfig {
+
+		private AuthorizationEndpointConfig() {
+		}
+
+		public AuthorizationEndpointConfig authorizationRequestBuilder(AuthorizationRequestUriBuilder authorizationRequestBuilder) {
+			Assert.notNull(authorizationRequestBuilder, "authorizationRequestBuilder cannot be null");
+			OAuth2LoginConfigurer.this.authorizationCodeRequestRedirectFilterConfigurer.authorizationRequestBuilder(authorizationRequestBuilder);
+			return this;
+		}
+
+		public <R extends RequestMatcher & RequestVariablesExtractor> AuthorizationEndpointConfig requestMatcher(R authorizationRequestMatcher) {
+			Assert.notNull(authorizationRequestMatcher, "authorizationRequestMatcher cannot be null");
+			OAuth2LoginConfigurer.this.authorizationCodeRequestRedirectFilterConfigurer.authorizationRequestMatcher(authorizationRequestMatcher);
+			return this;
+		}
+
+		public OAuth2LoginConfigurer<H> and() {
+			return OAuth2LoginConfigurer.this;
+		}
+	}
+
+	public RedirectionEndpointConfig redirectionEndpoint() {
+		return this.redirectionEndpoint;
+	}
+
+	public class RedirectionEndpointConfig {
+
+		private RedirectionEndpointConfig() {
+		}
+
+		public <R extends RequestMatcher & RequestVariablesExtractor> RedirectionEndpointConfig requestMatcher(R authorizationResponseMatcher) {
+			Assert.notNull(authorizationResponseMatcher, "authorizationResponseMatcher cannot be null");
+			OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.authorizationResponseMatcher(authorizationResponseMatcher);
+			return this;
+		}
+
+		public OAuth2LoginConfigurer<H> and() {
+			return OAuth2LoginConfigurer.this;
+		}
+	}
+
 	public UserInfoEndpointConfig userInfoEndpoint() {
 		return this.userInfoEndpointConfig;
 	}
@@ -94,33 +143,33 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 		private UserInfoEndpointConfig() {
 		}
 
-		public OAuth2LoginConfigurer<B> userInfoService(OAuth2UserService userInfoService) {
+		public UserInfoEndpointConfig userInfoService(OAuth2UserService userInfoService) {
 			Assert.notNull(userInfoService, "userInfoService cannot be null");
 			OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userInfoService(userInfoService);
-			return this.and();
+			return this;
 		}
 
-		public OAuth2LoginConfigurer<B> customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
+		public UserInfoEndpointConfig customUserType(Class<? extends OAuth2User> customUserType, URI userInfoUri) {
 			Assert.notNull(customUserType, "customUserType cannot be null");
 			Assert.notNull(userInfoUri, "userInfoUri cannot be null");
 			OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.customUserType(customUserType, userInfoUri);
-			return this.and();
+			return this;
 		}
 
-		public OAuth2LoginConfigurer<B> userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
+		public UserInfoEndpointConfig userNameAttributeName(String userNameAttributeName, URI userInfoUri) {
 			Assert.hasText(userNameAttributeName, "userNameAttributeName cannot be empty");
 			Assert.notNull(userInfoUri, "userInfoUri cannot be null");
 			OAuth2LoginConfigurer.this.authorizationCodeAuthenticationFilterConfigurer.userNameAttributeName(userNameAttributeName, userInfoUri);
-			return this.and();
+			return this;
 		}
 
-		public OAuth2LoginConfigurer<B> and() {
+		public OAuth2LoginConfigurer<H> and() {
 			return OAuth2LoginConfigurer.this;
 		}
 	}
 
 	@Override
-	public void init(B http) throws Exception {
+	public void init(H http) throws Exception {
 		this.authorizationCodeRequestRedirectFilterConfigurer.setBuilder(http);
 		this.authorizationCodeAuthenticationFilterConfigurer.setBuilder(http);
 
@@ -130,12 +179,12 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 	}
 
 	@Override
-	public void configure(B http) throws Exception {
+	public void configure(H http) throws Exception {
 		this.authorizationCodeRequestRedirectFilterConfigurer.configure(http);
 		this.authorizationCodeAuthenticationFilterConfigurer.configure(http);
 	}
 
-	static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getClientRegistrationRepository(B http) {
+	static <H extends HttpSecurityBuilder<H>> ClientRegistrationRepository getClientRegistrationRepository(H http) {
 		ClientRegistrationRepository clientRegistrationRepository = http.getSharedObject(ClientRegistrationRepository.class);
 		if (clientRegistrationRepository == null) {
 			clientRegistrationRepository = getDefaultClientRegistrationRepository(http);
@@ -144,17 +193,33 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> exten
 		return clientRegistrationRepository;
 	}
 
-	private static <B extends HttpSecurityBuilder<B>> ClientRegistrationRepository getDefaultClientRegistrationRepository(B http) {
+	private static <H extends HttpSecurityBuilder<H>> ClientRegistrationRepository getDefaultClientRegistrationRepository(H http) {
 		return http.getSharedObject(ApplicationContext.class).getBean(ClientRegistrationRepository.class);
 	}
 
-	private void initDefaultLoginFilter(B http) {
+	private void initDefaultLoginFilter(H http) {
 		DefaultLoginPageGeneratingFilter loginPageGeneratingFilter = http.getSharedObject(DefaultLoginPageGeneratingFilter.class);
 		if (loginPageGeneratingFilter != null && !this.authorizationCodeAuthenticationFilterConfigurer.isCustomLoginPage()) {
 			ClientRegistrationRepository clientRegistrationRepository = getClientRegistrationRepository(this.getBuilder());
 			if (!CollectionUtils.isEmpty(clientRegistrationRepository.getRegistrations())) {
+				String authorizationRequestBaseUri;
+				RequestMatcher authorizationRequestMatcher = OAuth2LoginConfigurer.this.authorizationCodeRequestRedirectFilterConfigurer.getAuthorizationRequestMatcher();
+				if (authorizationRequestMatcher != null && AntPathRequestMatcher.class.isAssignableFrom(authorizationRequestMatcher.getClass())) {
+					String authorizationRequestPattern =  ((AntPathRequestMatcher)authorizationRequestMatcher).getPattern();
+					String clientAliasTemplateVariable = "{" + CLIENT_ALIAS_URI_VARIABLE_NAME + "}";
+					if (authorizationRequestPattern.endsWith(clientAliasTemplateVariable)) {
+						authorizationRequestBaseUri = authorizationRequestPattern.substring(
+							0, authorizationRequestPattern.length() - clientAliasTemplateVariable.length() - 1);
+					} else {
+						authorizationRequestBaseUri = authorizationRequestPattern;
+					}
+				} else {
+					authorizationRequestBaseUri = AuthorizationCodeRequestRedirectFilter.DEFAULT_AUTHORIZATION_REQUEST_BASE_URI;
+				}
+
 				Map<String, String> oauth2AuthenticationUrlToClientName = clientRegistrationRepository.getRegistrations().stream()
-					.collect(Collectors.toMap(e -> AuthorizationCodeRequestRedirectFilter.AUTHORIZATION_BASE_URI + "/" + e.getClientAlias(),
+					.collect(Collectors.toMap(
+						e -> authorizationRequestBaseUri + "/" + e.getClientAlias(),
 						e -> e.getClientName()));
 				loginPageGeneratingFilter.setOauth2LoginEnabled(true);
 				loginPageGeneratingFilter.setOauth2AuthenticationUrlToClientName(oauth2AuthenticationUrlToClientName);

+ 14 - 7
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeAuthenticationProcessingFilter.java

@@ -30,6 +30,7 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
 import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
 import org.springframework.util.Assert;
 
 import javax.servlet.ServletException;
@@ -99,21 +100,21 @@ import java.io.IOException;
  * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.2">Section 4.1.2 Authorization Response</a>
  */
 public class AuthorizationCodeAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
-	public static final String AUTHORIZE_BASE_URI = "/oauth2/authorize/code";
-	private static final String CLIENT_ALIAS_VARIABLE_NAME = "clientAlias";
-	private static final String AUTHORIZE_URI = AUTHORIZE_BASE_URI + "/{" + CLIENT_ALIAS_VARIABLE_NAME + "}";
+	public static final String DEFAULT_AUTHORIZATION_RESPONSE_BASE_URI = "/oauth2/authorize/code";
+	public static final String CLIENT_ALIAS_URI_VARIABLE_NAME = "clientAlias";
+	public static final String DEFAULT_AUTHORIZATION_RESPONSE_URI = DEFAULT_AUTHORIZATION_RESPONSE_BASE_URI + "/{" + CLIENT_ALIAS_URI_VARIABLE_NAME + "}";
 	private static final String AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE = "authorization_request_not_found";
 	private static final String INVALID_STATE_PARAMETER_ERROR_CODE = "invalid_state_parameter";
 	private static final String INVALID_REDIRECT_URI_PARAMETER_ERROR_CODE = "invalid_redirect_uri_parameter";
 	private final ErrorResponseAttributesConverter errorResponseConverter = new ErrorResponseAttributesConverter();
 	private final AuthorizationCodeAuthorizationResponseAttributesConverter authorizationCodeResponseConverter =
 		new AuthorizationCodeAuthorizationResponseAttributesConverter();
-	private final RequestMatcher authorizeRequestMatcher = new AntPathRequestMatcher(AUTHORIZE_URI);
+	private RequestMatcher authorizationResponseMatcher = new AntPathRequestMatcher(DEFAULT_AUTHORIZATION_RESPONSE_URI);
 	private ClientRegistrationRepository clientRegistrationRepository;
 	private AuthorizationRequestRepository authorizationRequestRepository = new HttpSessionAuthorizationRequestRepository();
 
 	public AuthorizationCodeAuthenticationProcessingFilter() {
-		super(AUTHORIZE_URI);
+		super(DEFAULT_AUTHORIZATION_RESPONSE_URI);
 	}
 
 	@Override
@@ -157,8 +158,14 @@ public class AuthorizationCodeAuthenticationProcessingFilter extends AbstractAut
 		return authenticated;
 	}
 
-	public RequestMatcher getAuthorizeRequestMatcher() {
-		return this.authorizeRequestMatcher;
+	public RequestMatcher getAuthorizationResponseMatcher() {
+		return this.authorizationResponseMatcher;
+	}
+
+	public final <T extends RequestMatcher & RequestVariablesExtractor> void setAuthorizationResponseMatcher(T authorizationResponseMatcher) {
+		Assert.notNull(authorizationResponseMatcher, "authorizationResponseMatcher cannot be null");
+		this.authorizationResponseMatcher = authorizationResponseMatcher;
+		this.setRequiresAuthenticationRequestMatcher(authorizationResponseMatcher);
 	}
 
 	protected ClientRegistrationRepository getClientRegistrationRepository() {

+ 14 - 10
oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/authentication/AuthorizationCodeRequestRedirectFilter.java

@@ -22,6 +22,8 @@ import org.springframework.security.oauth2.core.endpoint.AuthorizationRequestAtt
 import org.springframework.security.web.DefaultRedirectStrategy;
 import org.springframework.security.web.RedirectStrategy;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.security.web.util.matcher.RequestVariablesExtractor;
 import org.springframework.util.Assert;
 import org.springframework.web.filter.OncePerRequestFilter;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -35,8 +37,6 @@ import java.net.URI;
 import java.util.HashMap;
 import java.util.Map;
 
-import static org.springframework.security.oauth2.client.authentication.AuthorizationCodeAuthenticationProcessingFilter.AUTHORIZE_BASE_URI;
-
 /**
  * This <code>Filter</code> initiates the authorization code grant flow by redirecting
  * the end-user's user-agent to the authorization server's <i>Authorization Endpoint</i>.
@@ -60,10 +60,10 @@ import static org.springframework.security.oauth2.client.authentication.Authoriz
  * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.1.1">Section 4.1.1 Authorization Request</a>
  */
 public class AuthorizationCodeRequestRedirectFilter extends OncePerRequestFilter {
-	public static final String AUTHORIZATION_BASE_URI = "/oauth2/authorization/code";
-	private static final String CLIENT_ALIAS_VARIABLE_NAME = "clientAlias";
-	private static final String AUTHORIZATION_URI = AUTHORIZATION_BASE_URI + "/{" + CLIENT_ALIAS_VARIABLE_NAME + "}";
-	private final AntPathRequestMatcher authorizationRequestMatcher;
+	public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization/code";
+	public static final String CLIENT_ALIAS_URI_VARIABLE_NAME = "clientAlias";
+	public static final String DEFAULT_AUTHORIZATION_REQUEST_URI = DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + CLIENT_ALIAS_URI_VARIABLE_NAME + "}";
+	private RequestMatcher authorizationRequestMatcher;
 	private final ClientRegistrationRepository clientRegistrationRepository;
 	private final AuthorizationRequestUriBuilder authorizationUriBuilder;
 	private final RedirectStrategy authorizationRedirectStrategy = new DefaultRedirectStrategy();
@@ -75,11 +75,16 @@ public class AuthorizationCodeRequestRedirectFilter extends OncePerRequestFilter
 
 		Assert.notNull(clientRegistrationRepository, "clientRegistrationRepository cannot be null");
 		Assert.notNull(authorizationUriBuilder, "authorizationUriBuilder cannot be null");
-		this.authorizationRequestMatcher = new AntPathRequestMatcher(AUTHORIZATION_URI);
+		this.authorizationRequestMatcher = new AntPathRequestMatcher(DEFAULT_AUTHORIZATION_REQUEST_URI);
 		this.clientRegistrationRepository = clientRegistrationRepository;
 		this.authorizationUriBuilder = authorizationUriBuilder;
 	}
 
+	public final <T extends RequestMatcher & RequestVariablesExtractor> void setAuthorizationRequestMatcher(T authorizationRequestMatcher) {
+		Assert.notNull(authorizationRequestMatcher, "authorizationRequestMatcher cannot be null");
+		this.authorizationRequestMatcher = authorizationRequestMatcher;
+	}
+
 	public final void setAuthorizationRequestRepository(AuthorizationRequestRepository authorizationRequestRepository) {
 		Assert.notNull(authorizationRequestRepository, "authorizationRequestRepository cannot be null");
 		this.authorizationRequestRepository = authorizationRequestRepository;
@@ -108,8 +113,8 @@ public class AuthorizationCodeRequestRedirectFilter extends OncePerRequestFilter
 	protected void sendRedirectForAuthorizationCode(HttpServletRequest request, HttpServletResponse response)
 			throws IOException, ServletException {
 
-		String clientAlias = this.authorizationRequestMatcher
-				.extractUriTemplateVariables(request).get(CLIENT_ALIAS_VARIABLE_NAME);
+		String clientAlias = ((RequestVariablesExtractor)this.authorizationRequestMatcher)
+				.extractUriTemplateVariables(request).get(CLIENT_ALIAS_URI_VARIABLE_NAME);
 		ClientRegistration clientRegistration = this.clientRegistrationRepository.getRegistrationByClientAlias(clientAlias);
 		if (clientRegistration == null) {
 			throw new IllegalArgumentException("Invalid Client Identifier (Alias): " + clientAlias);
@@ -146,7 +151,6 @@ public class AuthorizationCodeRequestRedirectFilter extends OncePerRequestFilter
 		uriVariables.put("scheme", request.getScheme());
 		uriVariables.put("serverName", request.getServerName());
 		uriVariables.put("serverPort", String.valueOf(request.getServerPort()));
-		uriVariables.put("baseAuthorizeUri", AUTHORIZE_BASE_URI);
 		uriVariables.put("clientAlias", clientRegistration.getClientAlias());
 
 		return UriComponentsBuilder.fromUriString(clientRegistration.getRedirectUri())

+ 4 - 4
samples/boot/oauth2login/src/main/resources/META-INF/oauth2-clients-defaults.yml

@@ -4,7 +4,7 @@ security:
       google:
         client-authentication-method: basic
         authorized-grant-type: authorization_code
-        redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}"
+        redirect-uri: "{scheme}://{serverName}:{serverPort}/oauth2/authorize/code/{clientAlias}"
         scopes: openid, profile, email, address, phone
         authorization-uri: "https://accounts.google.com/o/oauth2/auth"
         token-uri: "https://accounts.google.com/o/oauth2/token"
@@ -15,7 +15,7 @@ security:
       github:
         client-authentication-method: basic
         authorized-grant-type: authorization_code
-        redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}"
+        redirect-uri: "{scheme}://{serverName}:{serverPort}/oauth2/authorize/code/{clientAlias}"
         scopes: user
         authorization-uri: "https://github.com/login/oauth/authorize"
         token-uri: "https://github.com/login/oauth/access_token"
@@ -26,7 +26,7 @@ security:
       facebook:
         client-authentication-method: post
         authorized-grant-type: authorization_code
-        redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}"
+        redirect-uri: "{scheme}://{serverName}:{serverPort}/oauth2/authorize/code/{clientAlias}"
         scopes: public_profile, email
         authorization-uri: "https://www.facebook.com/v2.8/dialog/oauth"
         token-uri: "https://graph.facebook.com/v2.8/oauth/access_token"
@@ -37,7 +37,7 @@ security:
       okta:
         client-authentication-method: basic
         authorized-grant-type: authorization_code
-        redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}"
+        redirect-uri: "{scheme}://{serverName}:{serverPort}/oauth2/authorize/code/{clientAlias}"
         scopes: openid, profile, email, address, phone
         client-name: Okta
         client-alias: okta