Browse Source

AuthentictionWebFilter uses ServerWebExchangeMatcher

Fixes gh-4528
Rob Winch 8 years ago
parent
commit
7fd5239a75

+ 30 - 13
webflux/src/main/java/org/springframework/security/web/server/authentication/AuthenticationWebFilter.java

@@ -17,9 +17,13 @@
  */
 package org.springframework.security.web.server.authentication;
 
+import java.util.function.Function;
+
+import org.springframework.security.core.AuthenticationException;
+import reactor.core.publisher.Mono;
+
 import org.springframework.security.authentication.ReactiveAuthenticationManager;
 import org.springframework.security.core.Authentication;
-import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.context.SecurityContextImpl;
 import org.springframework.security.web.server.AuthenticationEntryPoint;
 import org.springframework.security.web.server.HttpBasicAuthenticationConverter;
@@ -27,13 +31,12 @@ import org.springframework.security.web.server.authentication.www.HttpBasicAuthe
 import org.springframework.security.web.server.context.SecurityContextRepository;
 import org.springframework.security.web.server.context.SecurityContextRepositoryServerWebExchange;
 import org.springframework.security.web.server.context.ServerWebExchangeAttributeSecurityContextRepository;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.WebFilter;
 import org.springframework.web.server.WebFilterChain;
-import reactor.core.publisher.Mono;
-
-import java.util.function.Function;
 
 /**
  *
@@ -52,6 +55,8 @@ public class AuthenticationWebFilter implements WebFilter {
 
 	private SecurityContextRepository securityContextRepository = new ServerWebExchangeAttributeSecurityContextRepository();
 
+	private ServerWebExchangeMatcher requiresAuthenticationMatcher = ServerWebExchangeMatchers.anyExchange();
+
 	public AuthenticationWebFilter(ReactiveAuthenticationManager authenticationManager) {
 		Assert.notNull(authenticationManager, "authenticationManager cannot be null");
 		this.authenticationManager = authenticationManager;
@@ -59,17 +64,23 @@ public class AuthenticationWebFilter implements WebFilter {
 
 	@Override
 	public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
-		ServerWebExchange wrappedExchange = wrap(exchange);
-		return this.authenticationConverter.apply(wrappedExchange)
-			.switchIfEmpty(Mono.defer(() -> chain.filter(wrappedExchange).cast(Authentication.class)))
-			.flatMap( token -> this.authenticationManager.authenticate(token)
-				.flatMap(authentication -> onAuthenticationSuccess(authentication, wrappedExchange, chain))
-				.onErrorResume( AuthenticationException.class, t -> this.entryPoint.commence(wrappedExchange, t))
-			);
+		ServerWebExchange wrappedExchange = new SecurityContextRepositoryServerWebExchange(exchange, this.securityContextRepository);
+		return filterInternal(wrappedExchange, chain);
 	}
 
-	private ServerWebExchange wrap(ServerWebExchange exchange) {
-		return new SecurityContextRepositoryServerWebExchange(exchange, this.securityContextRepository);
+	private Mono<Void> filterInternal(ServerWebExchange wrappedExchange, WebFilterChain chain) {
+		return this.requiresAuthenticationMatcher.matches(wrappedExchange)
+			.filter( matchResult -> matchResult.isMatch())
+			.flatMap( matchResult -> this.authenticationConverter.apply(wrappedExchange))
+			.switchIfEmpty(chain.filter(wrappedExchange).then(Mono.empty()))
+			.flatMap( token -> authenticate(wrappedExchange, chain, token));
+	}
+
+	private Mono<Void> authenticate(ServerWebExchange wrappedExchange,
+		WebFilterChain chain, Authentication token) {
+		return this.authenticationManager.authenticate(token)
+			.flatMap(authentication -> onAuthenticationSuccess(authentication, wrappedExchange, chain))
+			.onErrorResume(AuthenticationException.class, e -> this.entryPoint.commence(wrappedExchange, e));
 	}
 
 	private Mono<Void> onAuthenticationSuccess(Authentication authentication, ServerWebExchange exchange, WebFilterChain chain) {
@@ -96,4 +107,10 @@ public class AuthenticationWebFilter implements WebFilter {
 	public void setEntryPoint(AuthenticationEntryPoint entryPoint) {
 		this.entryPoint = entryPoint;
 	}
+
+	public void setRequiresAuthenticationMatcher(
+		ServerWebExchangeMatcher requiresAuthenticationMatcher) {
+		Assert.notNull(requiresAuthenticationMatcher, "requiresAuthenticationMatcher cannot be null");
+		this.requiresAuthenticationMatcher = requiresAuthenticationMatcher;
+	}
 }

+ 29 - 0
webflux/src/test/java/org/springframework/security/web/server/authentication/AuthenticationWebFilterTests.java

@@ -34,6 +34,7 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
 import org.springframework.security.web.server.AuthenticationEntryPoint;
 import org.springframework.security.web.server.context.SecurityContextRepository;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
 import org.springframework.test.web.reactive.server.EntityExchangeResult;
 import org.springframework.test.web.reactive.server.WebTestClient;
 import org.springframework.web.server.ServerWebExchange;
@@ -206,6 +207,29 @@ public class AuthenticationWebFilterTests {
 		verifyZeroInteractions(this.entryPoint);
 	}
 
+	@Test
+	public void filterWhenNotMatchAndConvertAndAuthenticationSuccessThenContinues() {
+		this.filter.setRequiresAuthenticationMatcher(e -> ServerWebExchangeMatcher.MatchResult.notMatch());
+
+		WebTestClient client = WebTestClientBuilder
+			.bindToWebFilters(this.filter)
+			.build();
+
+		EntityExchangeResult<String> result = client
+			.mutate()
+			.filter(basicAuthentication("test","this"))
+			.build()
+			.get()
+			.uri("/")
+			.exchange()
+			.expectStatus().isOk()
+			.expectBody(String.class).consumeWith(b -> assertThat(b.getResponseBody()).isEqualTo("ok"))
+			.returnResult();
+
+		assertThat(result.getResponseCookies()).isEmpty();
+		verifyZeroInteractions(this.authenticationConverter, this.authenticationManager, this.successHandler);
+	}
+
 	@Test
 	public void filterWhenConvertAndAuthenticationFailThenEntryPoint() {
 		Mono<Authentication> authentication = Mono.just(new TestingAuthenticationToken("test", "this", "ROLE_USER"));
@@ -250,4 +274,9 @@ public class AuthenticationWebFilterTests {
 		verify(this.securityContextRepository, never()).save(any(), any());
 		verifyZeroInteractions(this.successHandler, this.entryPoint);
 	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void setRequiresAuthenticationMatcherWhenNullThenException() {
+		this.filter.setRequiresAuthenticationMatcher(null);
+	}
 }