DelegatingAuthenticationEntryPoint uses RequestMatcherEntry
Closes gh-17915
This commit is contained in:
		
							parent
							
								
									c905ac359d
								
							
						
					
					
						commit
						9a3ae4b867
					
				|  | @ -18,6 +18,8 @@ package org.springframework.security.config.annotation.web.configurers; | ||||||
| 
 | 
 | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
| 
 | 
 | ||||||
|  | import org.jspecify.annotations.Nullable; | ||||||
|  | 
 | ||||||
| import org.springframework.security.config.Customizer; | import org.springframework.security.config.Customizer; | ||||||
| import org.springframework.security.config.annotation.web.HttpSecurityBuilder; | import org.springframework.security.config.annotation.web.HttpSecurityBuilder; | ||||||
| import org.springframework.security.config.annotation.web.builders.HttpSecurity; | import org.springframework.security.config.annotation.web.builders.HttpSecurity; | ||||||
|  | @ -71,7 +73,7 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> | ||||||
| 
 | 
 | ||||||
| 	private AccessDeniedHandler accessDeniedHandler; | 	private AccessDeniedHandler accessDeniedHandler; | ||||||
| 
 | 
 | ||||||
| 	private LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> defaultEntryPointMappings = new LinkedHashMap<>(); | 	private DelegatingAuthenticationEntryPoint.@Nullable Builder defaultEntryPoint; | ||||||
| 
 | 
 | ||||||
| 	private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>(); | 	private LinkedHashMap<RequestMatcher, AccessDeniedHandler> defaultDeniedHandlerMappings = new LinkedHashMap<>(); | ||||||
| 
 | 
 | ||||||
|  | @ -161,7 +163,10 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> | ||||||
| 	 */ | 	 */ | ||||||
| 	public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint, | 	public ExceptionHandlingConfigurer<H> defaultAuthenticationEntryPointFor(AuthenticationEntryPoint entryPoint, | ||||||
| 			RequestMatcher preferredMatcher) { | 			RequestMatcher preferredMatcher) { | ||||||
| 		this.defaultEntryPointMappings.put(preferredMatcher, entryPoint); | 		if (this.defaultEntryPoint == null) { | ||||||
|  | 			this.defaultEntryPoint = DelegatingAuthenticationEntryPoint.builder(); | ||||||
|  | 		} | ||||||
|  | 		this.defaultEntryPoint.addEntryPointFor(entryPoint, preferredMatcher); | ||||||
| 		return this; | 		return this; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -235,16 +240,10 @@ public final class ExceptionHandlingConfigurer<H extends HttpSecurityBuilder<H>> | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private AuthenticationEntryPoint createDefaultEntryPoint(H http) { | 	private AuthenticationEntryPoint createDefaultEntryPoint(H http) { | ||||||
| 		if (this.defaultEntryPointMappings.isEmpty()) { | 		if (this.defaultEntryPoint == null) { | ||||||
| 			return new Http403ForbiddenEntryPoint(); | 			return new Http403ForbiddenEntryPoint(); | ||||||
| 		} | 		} | ||||||
| 		if (this.defaultEntryPointMappings.size() == 1) { | 		return this.defaultEntryPoint.build(); | ||||||
| 			return this.defaultEntryPointMappings.values().iterator().next(); |  | ||||||
| 		} |  | ||||||
| 		DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint( |  | ||||||
| 				this.defaultEntryPointMappings); |  | ||||||
| 		entryPoint.setDefaultEntryPoint(this.defaultEntryPointMappings.values().iterator().next()); |  | ||||||
| 		return entryPoint; |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -18,7 +18,6 @@ package org.springframework.security.config.annotation.web.configurers; | ||||||
| 
 | 
 | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.LinkedHashMap; |  | ||||||
| 
 | 
 | ||||||
| import jakarta.servlet.http.HttpServletRequest; | import jakarta.servlet.http.HttpServletRequest; | ||||||
| 
 | 
 | ||||||
|  | @ -103,11 +102,12 @@ public final class HttpBasicConfigurer<B extends HttpSecurityBuilder<B>> | ||||||
| 	 */ | 	 */ | ||||||
| 	public HttpBasicConfigurer() { | 	public HttpBasicConfigurer() { | ||||||
| 		realmName(DEFAULT_REALM); | 		realmName(DEFAULT_REALM); | ||||||
| 		LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>(); | 		// @formatter:off | ||||||
| 		entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED)); | 		this.authenticationEntryPoint = DelegatingAuthenticationEntryPoint.builder() | ||||||
| 		DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints); | 				.addEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), X_REQUESTED_WITH) | ||||||
| 		defaultEntryPoint.setDefaultEntryPoint(this.basicAuthEntryPoint); | 				.defaultEntryPoint(this.basicAuthEntryPoint) | ||||||
| 		this.authenticationEntryPoint = defaultEntryPoint; | 				.build(); | ||||||
|  | 		// @formatter:on | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -19,7 +19,6 @@ package org.springframework.security.config.annotation.web.configurers.oauth2.cl | ||||||
| import java.lang.reflect.Field; | import java.lang.reflect.Field; | ||||||
| import java.util.Collections; | import java.util.Collections; | ||||||
| import java.util.HashMap; | import java.util.HashMap; | ||||||
| import java.util.LinkedHashMap; |  | ||||||
| import java.util.Map; | import java.util.Map; | ||||||
| 
 | 
 | ||||||
| import jakarta.servlet.http.HttpServletRequest; | import jakarta.servlet.http.HttpServletRequest; | ||||||
|  | @ -553,13 +552,15 @@ public final class OAuth2LoginConfigurer<B extends HttpSecurityBuilder<B>> | ||||||
| 		RequestMatcher notXRequestedWith = new NegatedRequestMatcher( | 		RequestMatcher notXRequestedWith = new NegatedRequestMatcher( | ||||||
| 				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); | 				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); | ||||||
| 		RequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http); | 		RequestMatcher formLoginNotEnabled = getFormLoginNotEnabledRequestMatcher(http); | ||||||
| 		LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>(); |  | ||||||
| 		LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage); | 		LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage); | ||||||
| 		entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher), | 		RequestMatcher loginUrlMatcher = new AndRequestMatcher(notXRequestedWith, | ||||||
| 				formLoginNotEnabled), loginUrlEntryPoint); | 				new NegatedRequestMatcher(defaultLoginPageMatcher), formLoginNotEnabled); | ||||||
| 		DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints); | 		// @formatter:off | ||||||
| 		loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint()); | 		return DelegatingAuthenticationEntryPoint.builder() | ||||||
| 		return loginEntryPoint; | 			.addEntryPointFor(loginUrlEntryPoint, loginUrlMatcher) | ||||||
|  | 			.defaultEntryPoint(getAuthenticationEntryPoint()) | ||||||
|  | 			.build(); | ||||||
|  | 		// @formatter:on | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private RequestMatcher getFormLoginNotEnabledRequestMatcher(B http) { | 	private RequestMatcher getFormLoginNotEnabledRequestMatcher(B http) { | ||||||
|  |  | ||||||
|  | @ -339,13 +339,15 @@ public final class Saml2LoginConfigurer<B extends HttpSecurityBuilder<B>> | ||||||
| 				new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher); | 				new OrRequestMatcher(loginPageMatcher, faviconMatcher), defaultEntryPointMatcher); | ||||||
| 		RequestMatcher notXRequestedWith = new NegatedRequestMatcher( | 		RequestMatcher notXRequestedWith = new NegatedRequestMatcher( | ||||||
| 				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); | 				new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest")); | ||||||
| 		LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap<>(); |  | ||||||
| 		LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage); | 		LoginUrlAuthenticationEntryPoint loginUrlEntryPoint = new LoginUrlAuthenticationEntryPoint(providerLoginPage); | ||||||
| 		entryPoints.put(new AndRequestMatcher(notXRequestedWith, new NegatedRequestMatcher(defaultLoginPageMatcher)), | 		RequestMatcher loginUrlMatcher = new AndRequestMatcher(notXRequestedWith, | ||||||
| 				loginUrlEntryPoint); | 				new NegatedRequestMatcher(defaultLoginPageMatcher)); | ||||||
| 		DelegatingAuthenticationEntryPoint loginEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints); | 		// @formatter:off | ||||||
| 		loginEntryPoint.setDefaultEntryPoint(this.getAuthenticationEntryPoint()); | 		return DelegatingAuthenticationEntryPoint.builder() | ||||||
| 		return loginEntryPoint; | 				.addEntryPointFor(loginUrlEntryPoint, loginUrlMatcher) | ||||||
|  | 				.defaultEntryPoint(getAuthenticationEntryPoint()) | ||||||
|  | 				.build(); | ||||||
|  | 		// @formatter:on | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	private void setAuthenticationRequestRepository(B http, | 	private void setAuthenticationRequestRepository(B http, | ||||||
|  |  | ||||||
|  | @ -17,13 +17,18 @@ | ||||||
| package org.springframework.security.web.authentication; | package org.springframework.security.web.authentication; | ||||||
| 
 | 
 | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  | import java.util.ArrayList; | ||||||
|  | import java.util.Arrays; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
|  | import java.util.List; | ||||||
|  | import java.util.stream.Collectors; | ||||||
| 
 | 
 | ||||||
| import jakarta.servlet.ServletException; | import jakarta.servlet.ServletException; | ||||||
| import jakarta.servlet.http.HttpServletRequest; | import jakarta.servlet.http.HttpServletRequest; | ||||||
| import jakarta.servlet.http.HttpServletResponse; | import jakarta.servlet.http.HttpServletResponse; | ||||||
| import org.apache.commons.logging.Log; | import org.apache.commons.logging.Log; | ||||||
| import org.apache.commons.logging.LogFactory; | import org.apache.commons.logging.LogFactory; | ||||||
|  | import org.jspecify.annotations.Nullable; | ||||||
| 
 | 
 | ||||||
| import org.springframework.beans.factory.InitializingBean; | import org.springframework.beans.factory.InitializingBean; | ||||||
| import org.springframework.core.log.LogMessage; | import org.springframework.core.log.LogMessage; | ||||||
|  | @ -32,6 +37,7 @@ import org.springframework.security.web.AuthenticationEntryPoint; | ||||||
| import org.springframework.security.web.util.matcher.ELRequestMatcher; | import org.springframework.security.web.util.matcher.ELRequestMatcher; | ||||||
| import org.springframework.security.web.util.matcher.RequestMatcher; | import org.springframework.security.web.util.matcher.RequestMatcher; | ||||||
| import org.springframework.security.web.util.matcher.RequestMatcherEditor; | import org.springframework.security.web.util.matcher.RequestMatcherEditor; | ||||||
|  | import org.springframework.security.web.util.matcher.RequestMatcherEntry; | ||||||
| import org.springframework.util.Assert; | import org.springframework.util.Assert; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -64,22 +70,63 @@ public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPo | ||||||
| 
 | 
 | ||||||
| 	private static final Log logger = LogFactory.getLog(DelegatingAuthenticationEntryPoint.class); | 	private static final Log logger = LogFactory.getLog(DelegatingAuthenticationEntryPoint.class); | ||||||
| 
 | 
 | ||||||
| 	private final LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints; | 	private final List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints; | ||||||
| 
 | 
 | ||||||
| 	@SuppressWarnings("NullAway.Init") | 	@SuppressWarnings("NullAway.Init") | ||||||
| 	private AuthenticationEntryPoint defaultEntryPoint; | 	private AuthenticationEntryPoint defaultEntryPoint; | ||||||
| 
 | 
 | ||||||
| 	public DelegatingAuthenticationEntryPoint(LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) { | 	/** | ||||||
|  | 	 * Creates a new instance with the provided mappings. | ||||||
|  | 	 * @param entryPoints the mapping of {@link RequestMatcher} to | ||||||
|  | 	 * {@link AuthenticationEntryPoint}. Cannot be null or empty. | ||||||
|  | 	 * @param defaultEntryPoint the default {@link AuthenticationEntryPoint}. Cannot be | ||||||
|  | 	 * null. | ||||||
|  | 	 */ | ||||||
|  | 	public DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint defaultEntryPoint, | ||||||
|  | 			RequestMatcherEntry<AuthenticationEntryPoint>... entryPoints) { | ||||||
|  | 		Assert.notEmpty(entryPoints, "entryPoints cannot be empty"); | ||||||
|  | 		Assert.notNull(defaultEntryPoint, "defaultEntryPoint cannot be null"); | ||||||
|  | 		this.entryPoints = Arrays.asList(entryPoints); | ||||||
|  | 		this.defaultEntryPoint = defaultEntryPoint; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Creates a new instance with the provided mappings. | ||||||
|  | 	 * @param defaultEntryPoint the default {@link AuthenticationEntryPoint}. Cannot be | ||||||
|  | 	 * null. | ||||||
|  | 	 * @param entryPoints the mapping of {@link RequestMatcher} to | ||||||
|  | 	 * {@link AuthenticationEntryPoint}. Cannot be null or empty. | ||||||
|  | 	 */ | ||||||
|  | 	public DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint defaultEntryPoint, | ||||||
|  | 			List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints) { | ||||||
|  | 		Assert.notEmpty(entryPoints, "entryPoints cannot be empty"); | ||||||
|  | 		Assert.notNull(defaultEntryPoint, "defaultEntryPoint cannot be null"); | ||||||
| 		this.entryPoints = entryPoints; | 		this.entryPoints = entryPoints; | ||||||
|  | 		this.defaultEntryPoint = defaultEntryPoint; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Creates a new instance. | ||||||
|  | 	 * @param entryPoints | ||||||
|  | 	 * @deprecated Use | ||||||
|  | 	 * {@link #DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint, List)} | ||||||
|  | 	 */ | ||||||
|  | 	@Deprecated(forRemoval = true) | ||||||
|  | 	public DelegatingAuthenticationEntryPoint(LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints) { | ||||||
|  | 		this.entryPoints = entryPoints.entrySet() | ||||||
|  | 			.stream() | ||||||
|  | 			.map((e) -> new RequestMatcherEntry<>(e.getKey(), e.getValue())) | ||||||
|  | 			.collect(Collectors.toList()); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Override | 	@Override | ||||||
| 	public void commence(HttpServletRequest request, HttpServletResponse response, | 	public void commence(HttpServletRequest request, HttpServletResponse response, | ||||||
| 			AuthenticationException authException) throws IOException, ServletException { | 			AuthenticationException authException) throws IOException, ServletException { | ||||||
| 		for (RequestMatcher requestMatcher : this.entryPoints.keySet()) { | 		for (RequestMatcherEntry<AuthenticationEntryPoint> entry : this.entryPoints) { | ||||||
|  | 			RequestMatcher requestMatcher = entry.getRequestMatcher(); | ||||||
| 			logger.debug(LogMessage.format("Trying to match using %s", requestMatcher)); | 			logger.debug(LogMessage.format("Trying to match using %s", requestMatcher)); | ||||||
| 			if (requestMatcher.matches(request)) { | 			if (requestMatcher.matches(request)) { | ||||||
| 				AuthenticationEntryPoint entryPoint = this.entryPoints.get(requestMatcher); | 				AuthenticationEntryPoint entryPoint = entry.getEntry(); | ||||||
| 				logger.debug(LogMessage.format("Match found! Executing %s", entryPoint)); | 				logger.debug(LogMessage.format("Match found! Executing %s", entryPoint)); | ||||||
| 				entryPoint.commence(request, response, authException); | 				entryPoint.commence(request, response, authException); | ||||||
| 				return; | 				return; | ||||||
|  | @ -92,7 +139,10 @@ public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPo | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * EntryPoint which is used when no RequestMatcher returned true | 	 * EntryPoint which is used when no RequestMatcher returned true | ||||||
|  | 	 * @deprecated Use | ||||||
|  | 	 * {@link #DelegatingAuthenticationEntryPoint(AuthenticationEntryPoint, List)} | ||||||
| 	 */ | 	 */ | ||||||
|  | 	@Deprecated(forRemoval = true) | ||||||
| 	public void setDefaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint) { | 	public void setDefaultEntryPoint(AuthenticationEntryPoint defaultEntryPoint) { | ||||||
| 		this.defaultEntryPoint = defaultEntryPoint; | 		this.defaultEntryPoint = defaultEntryPoint; | ||||||
| 	} | 	} | ||||||
|  | @ -103,4 +153,76 @@ public class DelegatingAuthenticationEntryPoint implements AuthenticationEntryPo | ||||||
| 		Assert.notNull(this.defaultEntryPoint, "defaultEntryPoint must be specified"); | 		Assert.notNull(this.defaultEntryPoint, "defaultEntryPoint must be specified"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Creates a new {@link Builder} | ||||||
|  | 	 * @return the new {@link Builder} | ||||||
|  | 	 */ | ||||||
|  | 	public static Builder builder() { | ||||||
|  | 		return new Builder(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Used to build a new instance of {@link DelegatingAuthenticationEntryPoint}. | ||||||
|  | 	 * | ||||||
|  | 	 * @author Rob Winch | ||||||
|  | 	 * @since 7.0 | ||||||
|  | 	 */ | ||||||
|  | 	public static class Builder { | ||||||
|  | 
 | ||||||
|  | 		private @Nullable AuthenticationEntryPoint defaultEntryPoint; | ||||||
|  | 
 | ||||||
|  | 		private List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = new ArrayList<RequestMatcherEntry<AuthenticationEntryPoint>>(); | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Set the default {@link AuthenticationEntryPoint} if none match. The default is | ||||||
|  | 		 * to use the first {@link AuthenticationEntryPoint} added in | ||||||
|  | 		 * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}. | ||||||
|  | 		 * @param defaultEntryPoint the default {@link AuthenticationEntryPoint} to use. | ||||||
|  | 		 * @return the {@link Builder} for further customization. | ||||||
|  | 		 */ | ||||||
|  | 		public Builder defaultEntryPoint(@Nullable AuthenticationEntryPoint defaultEntryPoint) { | ||||||
|  | 			this.defaultEntryPoint = defaultEntryPoint; | ||||||
|  | 			return this; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Adds an {@link AuthenticationEntryPoint} for the provided | ||||||
|  | 		 * {@link RequestMatcher}. | ||||||
|  | 		 * @param entryPoint the {@link AuthenticationEntryPoint} to use. Cannot be null. | ||||||
|  | 		 * @param requestMatcher the {@link RequestMatcher} to use. Cannot be null. | ||||||
|  | 		 * @return the {@link Builder} for further customization. | ||||||
|  | 		 */ | ||||||
|  | 		public Builder addEntryPointFor(AuthenticationEntryPoint entryPoint, RequestMatcher requestMatcher) { | ||||||
|  | 			Assert.notNull(entryPoint, "entryPoint cannot be null"); | ||||||
|  | 			Assert.notNull(requestMatcher, "requestMatcher cannot be null"); | ||||||
|  | 			this.entryPoints.add(new RequestMatcherEntry<>(requestMatcher, entryPoint)); | ||||||
|  | 			return this; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		/** | ||||||
|  | 		 * Builds the {@link AuthenticationEntryPoint}. If the | ||||||
|  | 		 * {@link #defaultEntryPoint(AuthenticationEntryPoint)} is not set, then the first | ||||||
|  | 		 * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)} is used as | ||||||
|  | 		 * the default. If the {@link #defaultEntryPoint(AuthenticationEntryPoint)} is not | ||||||
|  | 		 * set and there is only a single | ||||||
|  | 		 * {@link #addEntryPointFor(AuthenticationEntryPoint, RequestMatcher)}, then the | ||||||
|  | 		 * {@link AuthenticationEntryPoint} is returned rather than wrapping it in | ||||||
|  | 		 * {@link DelegatingAuthenticationEntryPoint}. | ||||||
|  | 		 * @return the {@link AuthenticationEntryPoint} to use. | ||||||
|  | 		 */ | ||||||
|  | 		public AuthenticationEntryPoint build() { | ||||||
|  | 			Assert.notEmpty(this.entryPoints, "entryPoints cannot be empty"); | ||||||
|  | 			AuthenticationEntryPoint defaultEntryPoint = this.defaultEntryPoint; | ||||||
|  | 			if (defaultEntryPoint == null) { | ||||||
|  | 				AuthenticationEntryPoint firstAuthenticationEntryPoint = this.entryPoints.get(0).getEntry(); | ||||||
|  | 				if (this.entryPoints.size() == 1) { | ||||||
|  | 					return firstAuthenticationEntryPoint; | ||||||
|  | 				} | ||||||
|  | 				defaultEntryPoint = firstAuthenticationEntryPoint; | ||||||
|  | 			} | ||||||
|  | 			return new DelegatingAuthenticationEntryPoint(defaultEntryPoint, this.entryPoints); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,7 +16,9 @@ | ||||||
| 
 | 
 | ||||||
| package org.springframework.security.web.authentication; | package org.springframework.security.web.authentication; | ||||||
| 
 | 
 | ||||||
|  | import java.util.Collections; | ||||||
| import java.util.LinkedHashMap; | import java.util.LinkedHashMap; | ||||||
|  | import java.util.List; | ||||||
| 
 | 
 | ||||||
| import jakarta.servlet.http.HttpServletRequest; | import jakarta.servlet.http.HttpServletRequest; | ||||||
| import org.junit.jupiter.api.BeforeEach; | import org.junit.jupiter.api.BeforeEach; | ||||||
|  | @ -25,7 +27,9 @@ import org.junit.jupiter.api.Test; | ||||||
| import org.springframework.mock.web.MockHttpServletRequest; | import org.springframework.mock.web.MockHttpServletRequest; | ||||||
| import org.springframework.security.web.AuthenticationEntryPoint; | import org.springframework.security.web.AuthenticationEntryPoint; | ||||||
| import org.springframework.security.web.util.matcher.RequestMatcher; | import org.springframework.security.web.util.matcher.RequestMatcher; | ||||||
|  | import org.springframework.security.web.util.matcher.RequestMatcherEntry; | ||||||
| 
 | 
 | ||||||
|  | import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; | ||||||
| import static org.mockito.BDDMockito.given; | import static org.mockito.BDDMockito.given; | ||||||
| import static org.mockito.Mockito.mock; | import static org.mockito.Mockito.mock; | ||||||
| import static org.mockito.Mockito.never; | import static org.mockito.Mockito.never; | ||||||
|  | @ -52,8 +56,6 @@ public class DelegatingAuthenticationEntryPointTests { | ||||||
| 	public void before() { | 	public void before() { | ||||||
| 		this.defaultEntryPoint = mock(AuthenticationEntryPoint.class); | 		this.defaultEntryPoint = mock(AuthenticationEntryPoint.class); | ||||||
| 		this.entryPoints = new LinkedHashMap<>(); | 		this.entryPoints = new LinkedHashMap<>(); | ||||||
| 		this.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints); |  | ||||||
| 		this.daep.setDefaultEntryPoint(this.defaultEntryPoint); |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@Test | 	@Test | ||||||
|  | @ -62,6 +64,8 @@ public class DelegatingAuthenticationEntryPointTests { | ||||||
| 		RequestMatcher firstRM = mock(RequestMatcher.class); | 		RequestMatcher firstRM = mock(RequestMatcher.class); | ||||||
| 		given(firstRM.matches(this.request)).willReturn(false); | 		given(firstRM.matches(this.request)).willReturn(false); | ||||||
| 		this.entryPoints.put(firstRM, firstAEP); | 		this.entryPoints.put(firstRM, firstAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints); | ||||||
|  | 		this.daep.setDefaultEntryPoint(this.defaultEntryPoint); | ||||||
| 		this.daep.commence(this.request, null, null); | 		this.daep.commence(this.request, null, null); | ||||||
| 		verify(this.defaultEntryPoint).commence(this.request, null, null); | 		verify(this.defaultEntryPoint).commence(this.request, null, null); | ||||||
| 		verify(firstAEP, never()).commence(this.request, null, null); | 		verify(firstAEP, never()).commence(this.request, null, null); | ||||||
|  | @ -76,6 +80,8 @@ public class DelegatingAuthenticationEntryPointTests { | ||||||
| 		given(firstRM.matches(this.request)).willReturn(true); | 		given(firstRM.matches(this.request)).willReturn(true); | ||||||
| 		this.entryPoints.put(firstRM, firstAEP); | 		this.entryPoints.put(firstRM, firstAEP); | ||||||
| 		this.entryPoints.put(secondRM, secondAEP); | 		this.entryPoints.put(secondRM, secondAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints); | ||||||
|  | 		this.daep.setDefaultEntryPoint(this.defaultEntryPoint); | ||||||
| 		this.daep.commence(this.request, null, null); | 		this.daep.commence(this.request, null, null); | ||||||
| 		verify(firstAEP).commence(this.request, null, null); | 		verify(firstAEP).commence(this.request, null, null); | ||||||
| 		verify(secondAEP, never()).commence(this.request, null, null); | 		verify(secondAEP, never()).commence(this.request, null, null); | ||||||
|  | @ -93,6 +99,103 @@ public class DelegatingAuthenticationEntryPointTests { | ||||||
| 		given(secondRM.matches(this.request)).willReturn(true); | 		given(secondRM.matches(this.request)).willReturn(true); | ||||||
| 		this.entryPoints.put(firstRM, firstAEP); | 		this.entryPoints.put(firstRM, firstAEP); | ||||||
| 		this.entryPoints.put(secondRM, secondAEP); | 		this.entryPoints.put(secondRM, secondAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.entryPoints); | ||||||
|  | 		this.daep.setDefaultEntryPoint(this.defaultEntryPoint); | ||||||
|  | 		this.daep.commence(this.request, null, null); | ||||||
|  | 		verify(secondAEP).commence(this.request, null, null); | ||||||
|  | 		verify(firstAEP, never()).commence(this.request, null, null); | ||||||
|  | 		verify(this.defaultEntryPoint, never()).commence(this.request, null, null); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepListWhenNullEntryPoints() { | ||||||
|  | 		List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = null; | ||||||
|  | 		assertThatIllegalArgumentException().isThrownBy( | ||||||
|  | 				() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepListWhenEmptyEntryPoints() { | ||||||
|  | 		assertThatIllegalArgumentException() | ||||||
|  | 			.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), | ||||||
|  | 					Collections.emptyList())); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepListWhenNullDefaultEntryPoint() { | ||||||
|  | 		AuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher matcher = mock(RequestMatcher.class); | ||||||
|  | 		List<RequestMatcherEntry<AuthenticationEntryPoint>> entryPoints = List | ||||||
|  | 			.of(new RequestMatcherEntry<>(matcher, entryPoint)); | ||||||
|  | 		assertThatIllegalArgumentException() | ||||||
|  | 			.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(null, entryPoints)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepVargsWhenNullEntryPoints() { | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = null; | ||||||
|  | 		assertThatIllegalArgumentException().isThrownBy( | ||||||
|  | 				() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepVargsWhenEmptyEntryPoints() { | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = new RequestMatcherEntry[0]; | ||||||
|  | 		assertThatIllegalArgumentException().isThrownBy( | ||||||
|  | 				() -> new DelegatingAuthenticationEntryPoint(mock(AuthenticationEntryPoint.class), entryPoints)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void constructorAepVargsWhenNullDefaultEntryPoint() { | ||||||
|  | 		AuthenticationEntryPoint entryPoint = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher matcher = mock(RequestMatcher.class); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint>[] entryPoints = new RequestMatcherEntry[] { | ||||||
|  | 				new RequestMatcherEntry<>(matcher, entryPoint) }; | ||||||
|  | 		assertThatIllegalArgumentException() | ||||||
|  | 			.isThrownBy(() -> new DelegatingAuthenticationEntryPoint(null, entryPoints)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void commenceWhenNoMatchThenDefaultEntryPoint() throws Exception { | ||||||
|  | 		AuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher firstRM = mock(RequestMatcher.class); | ||||||
|  | 		given(firstRM.matches(this.request)).willReturn(false); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint> entry = new RequestMatcherEntry<>(firstRM, firstAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, entry); | ||||||
|  | 		this.daep.commence(this.request, null, null); | ||||||
|  | 		verify(this.defaultEntryPoint).commence(this.request, null, null); | ||||||
|  | 		verify(firstAEP, never()).commence(this.request, null, null); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void commenceWhenMatchFirstEntryPointThenOthersNotInvoked() throws Exception { | ||||||
|  | 		AuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher firstRM = mock(RequestMatcher.class); | ||||||
|  | 		given(firstRM.matches(this.request)).willReturn(true); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint> firstEntry = new RequestMatcherEntry<>(firstRM, firstAEP); | ||||||
|  | 		AuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher secondRM = mock(RequestMatcher.class); | ||||||
|  | 		given(secondRM.matches(this.request)).willReturn(false); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint> secondEntry = new RequestMatcherEntry<>(firstRM, firstAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, firstEntry, secondEntry); | ||||||
|  | 		this.daep.commence(this.request, null, null); | ||||||
|  | 		verify(firstAEP).commence(this.request, null, null); | ||||||
|  | 		verify(secondAEP, never()).commence(this.request, null, null); | ||||||
|  | 		verify(this.defaultEntryPoint, never()).commence(this.request, null, null); | ||||||
|  | 		verify(secondRM, never()).matches(this.request); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@Test | ||||||
|  | 	public void commenceWhenSecondMatchesThenDefaultNotInvoked() throws Exception { | ||||||
|  | 		AuthenticationEntryPoint firstAEP = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher firstRM = mock(RequestMatcher.class); | ||||||
|  | 		given(firstRM.matches(this.request)).willReturn(false); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint> firstEntry = new RequestMatcherEntry<>(firstRM, firstAEP); | ||||||
|  | 		AuthenticationEntryPoint secondAEP = mock(AuthenticationEntryPoint.class); | ||||||
|  | 		RequestMatcher secondRM = mock(RequestMatcher.class); | ||||||
|  | 		given(secondRM.matches(this.request)).willReturn(true); | ||||||
|  | 		RequestMatcherEntry<AuthenticationEntryPoint> secondEntry = new RequestMatcherEntry<>(secondRM, secondAEP); | ||||||
|  | 		this.daep = new DelegatingAuthenticationEntryPoint(this.defaultEntryPoint, firstEntry, secondEntry); | ||||||
| 		this.daep.commence(this.request, null, null); | 		this.daep.commence(this.request, null, null); | ||||||
| 		verify(secondAEP).commence(this.request, null, null); | 		verify(secondAEP).commence(this.request, null, null); | ||||||
| 		verify(firstAEP, never()).commence(this.request, null, null); | 		verify(firstAEP, never()).commence(this.request, null, null); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue