瀏覽代碼

ServerWebExchangeMatcher returns Mono<MatchResult>

Rob Winch 8 年之前
父節點
當前提交
3440909fc9

+ 7 - 10
webflux/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java

@@ -23,6 +23,7 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
 import org.springframework.web.server.ServerWebExchange;
 
+import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 import java.util.LinkedHashMap;
@@ -41,16 +42,12 @@ public class DelegatingReactiveAuthorizationManager implements ReactiveAuthoriza
 
 	@Override
 	public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, ServerWebExchange exchange) {
-		for(Map.Entry<ServerWebExchangeMatcher, ReactiveAuthorizationManager<AuthorizationContext>> entry : mappings.entrySet()) {
-			ServerWebExchangeMatcher matcher = entry.getKey();
-			ServerWebExchangeMatcher.MatchResult match = matcher.matches(exchange);
-			if(match.isMatch()) {
-				Map<String,Object> variables = match.getVariables();
-				AuthorizationContext context = new AuthorizationContext(exchange, variables);
-				return entry.getValue().check(authentication, context);
-			}
-		}
-		return Mono.just(new AuthorizationDecision(false));
+		return Flux.fromIterable(mappings.entrySet())
+			.concatMap(entry -> entry.getKey().matches(exchange)
+			.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
+			.flatMap(r -> entry.getValue().check(authentication, new AuthorizationContext(exchange, r.getVariables()))))
+			.next()
+			.defaultIfEmpty(new AuthorizationDecision(false));
 	}
 
 	public static DelegatingReactiveAuthorizationManager.Builder builder() {

+ 11 - 6
webflux/src/main/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcher.java

@@ -19,6 +19,8 @@ package org.springframework.security.web.server.util.matcher;
 
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -45,12 +47,15 @@ public class AndServerWebExchangeMatcher implements ServerWebExchangeMatcher {
 	 * @see org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher#matches(org.springframework.web.server.ServerWebExchange)
 	 */
 	@Override
-	public MatchResult matches(ServerWebExchange exchange) {
-		Map<String, Object> variables = new HashMap<>();
-		return matchers.stream()
-			.map(m -> m.matches(exchange))
-			.peek( m -> variables.putAll(m.getVariables()))
-			.allMatch(m -> m.isMatch()) ? MatchResult.match(variables) : MatchResult.notMatch();
+	public Mono<MatchResult> matches(ServerWebExchange exchange) {
+		return Mono.defer(() -> {
+			Map<String, Object> variables = new HashMap<>();
+			return Flux.fromIterable(matchers)
+				.flatMap(matcher -> matcher.matches(exchange))
+				.doOnNext(matchResult -> variables.putAll(matchResult.getVariables()))
+				.all(MatchResult::isMatch)
+				.flatMap(allMatch -> allMatch ? MatchResult.match(variables) : MatchResult.notMatch());
+		});
 	}
 
 	@Override

+ 7 - 5
webflux/src/main/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcher.java

@@ -23,6 +23,8 @@ import java.util.function.Predicate;
 
 import org.springframework.util.Assert;
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
 
 /**
  * @author Rob Winch
@@ -45,12 +47,12 @@ public class OrServerWebExchangeMatcher implements ServerWebExchangeMatcher {
 	 * @see org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher#matches(org.springframework.web.server.ServerWebExchange)
 	 */
 	@Override
-	public MatchResult matches(ServerWebExchange exchange) {
-		return matchers.stream()
-			.map(m -> m.matches(exchange))
+	public Mono<MatchResult> matches(ServerWebExchange exchange) {
+		return Flux.fromIterable(matchers)
+			.flatMap(m -> m.matches(exchange))
 			.filter(m -> m.isMatch())
-			.findFirst()
-			.orElse(MatchResult.notMatch());
+			.next()
+			.switchIfEmpty(MatchResult.notMatch());
 	}
 
 	@Override

+ 2 - 1
webflux/src/main/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcher.java

@@ -27,6 +27,7 @@ import org.springframework.util.Assert;
 import org.springframework.util.PathMatcher;
 import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.support.HttpRequestPathHelper;
+import reactor.core.publisher.Mono;
 
 /**
  * @author Rob Winch
@@ -51,7 +52,7 @@ public final class PathMatcherServerWebExchangeMatcher implements ServerWebExcha
 	}
 
 	@Override
-	public MatchResult matches(ServerWebExchange exchange) {
+	public Mono<MatchResult> matches(ServerWebExchange exchange) {
 		ServerHttpRequest request = exchange.getRequest();
 		if(this.method != null && !this.method.equals(request.getMethod())) {
 			return MatchResult.notMatch();

+ 7 - 6
webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatcher.java

@@ -21,6 +21,7 @@ import java.util.Collections;
 import java.util.Map;
 
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
 
 /**
  *
@@ -29,7 +30,7 @@ import org.springframework.web.server.ServerWebExchange;
  */
 public interface ServerWebExchangeMatcher {
 
-	MatchResult matches(ServerWebExchange exchange);
+	Mono<MatchResult> matches(ServerWebExchange exchange);
 
 	class MatchResult {
 		private final boolean match;
@@ -48,16 +49,16 @@ public interface ServerWebExchangeMatcher {
 			return variables;
 		}
 
-		public static MatchResult match() {
+		public static Mono<MatchResult> match() {
 			return match(Collections.emptyMap());
 		}
 
-		public static MatchResult match(Map<String,Object> variables) {
-			return new MatchResult(true, variables);
+		public static Mono<MatchResult> match(Map<String,Object> variables) {
+			return Mono.just(new MatchResult(true, variables));
 		}
 
-		public static MatchResult notMatch() {
-			return new MatchResult(false, Collections.emptyMap());
+		public static Mono<MatchResult> notMatch() {
+			return Mono.just(new MatchResult(false, Collections.emptyMap()));
 		}
 	}
 }

+ 2 - 1
webflux/src/main/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchers.java

@@ -19,6 +19,7 @@ package org.springframework.security.web.server.util.matcher;
 
 import org.springframework.http.HttpMethod;
 import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -48,7 +49,7 @@ public abstract class ServerWebExchangeMatchers {
 	public static ServerWebExchangeMatcher anyExchange() {
 		return new ServerWebExchangeMatcher() {
 			@Override
-			public MatchResult matches(ServerWebExchange exchange) {
+			public Mono<MatchResult> matches(ServerWebExchange exchange) {
 				return ServerWebExchangeMatcher.MatchResult.match();
 			}
 		};

+ 90 - 0
webflux/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java

@@ -0,0 +1,90 @@
+/*
+ *
+ *  * Copyright 2002-2017 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
+ *  *
+ *  *      http://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.web.server.authorization;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.springframework.security.authorization.AuthorityAuthorizationManager;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+/**
+ * @author Rob Winch
+ * @since 5.0
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class DelegatingReactiveAuthorizationManagerTests {
+	@Mock
+	ServerWebExchangeMatcher match1;
+	@Mock
+	ServerWebExchangeMatcher match2;
+	@Mock
+	AuthorityAuthorizationManager<AuthorizationContext> delegate1;
+	@Mock
+	AuthorityAuthorizationManager<AuthorizationContext> delegate2;
+	@Mock
+	ServerWebExchange exchange;
+	@Mock
+	Mono<Authentication> authentication;
+	@Mock
+	AuthorizationDecision decision;
+
+	DelegatingReactiveAuthorizationManager manager;
+
+	@Before
+	public void setup() {
+		manager = DelegatingReactiveAuthorizationManager.builder()
+			.add(match1, delegate1)
+			.add(match2, delegate2)
+			.build();
+	}
+
+	@Test
+	public void checkWhenFirstMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() {
+		when(match1.matches(any())).thenReturn(ServerWebExchangeMatcher.MatchResult.match());
+		when(delegate1.check(eq(authentication), any(AuthorizationContext.class))).thenReturn(Mono.just(decision));
+
+		assertThat(manager.check(authentication, exchange).block()).isEqualTo(decision);
+
+		verifyZeroInteractions(match2, delegate2);
+	}
+
+	@Test
+	public void checkWhenSecondMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() {
+		when(match1.matches(any())).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
+		when(match2.matches(any())).thenReturn(ServerWebExchangeMatcher.MatchResult.match());
+		when(delegate2.check(eq(authentication), any(AuthorizationContext.class))).thenReturn(Mono.just(decision));
+
+		assertThat(manager.check(authentication, exchange).block()).isEqualTo(decision);
+
+		verifyZeroInteractions(delegate1);
+	}
+}

+ 4 - 4
webflux/src/test/java/org/springframework/security/web/server/util/matcher/AndServerWebExchangeMatcherTests.java

@@ -61,7 +61,7 @@ public class AndServerWebExchangeMatcherTests {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.match(params1));
 		when(matcher2.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.match(params2));
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isTrue();
 		assertThat(matches.getVariables()).hasSize(2);
@@ -76,7 +76,7 @@ public class AndServerWebExchangeMatcherTests {
 	public void matchesWhenFalseFalseThenFalseAndMatcher2NotInvoked() throws Exception {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isFalse();
 		assertThat(matches.getVariables()).isEmpty();
@@ -91,7 +91,7 @@ public class AndServerWebExchangeMatcherTests {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.match(params));
 		when(matcher2.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isFalse();
 		assertThat(matches.getVariables()).isEmpty();
@@ -104,7 +104,7 @@ public class AndServerWebExchangeMatcherTests {
 	public void matchesWhenFalseTrueThenFalse() throws Exception {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isFalse();
 		assertThat(matches.getVariables()).isEmpty();

+ 3 - 3
webflux/src/test/java/org/springframework/security/web/server/util/matcher/OrServerWebExchangeMatcherTests.java

@@ -59,7 +59,7 @@ public class OrServerWebExchangeMatcherTests {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 		when(matcher2.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isFalse();
 		assertThat(matches.getVariables()).isEmpty();
@@ -73,7 +73,7 @@ public class OrServerWebExchangeMatcherTests {
 		Map<String, Object> params = Collections.singletonMap("foo", "bar");
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.match(params));
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isTrue();
 		assertThat(matches.getVariables()).isEqualTo(params);
@@ -88,7 +88,7 @@ public class OrServerWebExchangeMatcherTests {
 		when(matcher1.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.notMatch());
 		when(matcher2.matches(exchange)).thenReturn(ServerWebExchangeMatcher.MatchResult.match(params));
 
-		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange);
+		ServerWebExchangeMatcher.MatchResult matches = matcher.matches(exchange).block();
 
 		assertThat(matches.isMatch()).isTrue();
 		assertThat(matches.getVariables()).isEqualTo(params);

+ 4 - 4
webflux/src/test/java/org/springframework/security/web/server/util/matcher/PathMatcherServerWebExchangeMatcherTests.java

@@ -75,14 +75,14 @@ public class PathMatcherServerWebExchangeMatcherTests {
 	public void matchesWhenPathMatcherTrueThenReturnTrue() {
 		when(pathMatcher.match(pattern, path)).thenReturn(true);
 
-		assertThat(matcher.matches(exchange).isMatch()).isTrue();
+		assertThat(matcher.matches(exchange).block().isMatch()).isTrue();
 	}
 
 	@Test
 	public void matchesWhenPathMatcherFalseThenReturnFalse() {
 		when(pathMatcher.match(pattern, path)).thenReturn(false);
 
-		assertThat(matcher.matches(exchange).isMatch()).isFalse();
+		assertThat(matcher.matches(exchange).block().isMatch()).isFalse();
 
 		verify(pathMatcher).match(pattern, path);
 	}
@@ -93,7 +93,7 @@ public class PathMatcherServerWebExchangeMatcherTests {
 		matcher.setPathMatcher(pathMatcher);
 		when(pathMatcher.match(pattern, path)).thenReturn(true);
 
-		assertThat(matcher.matches(exchange).isMatch()).isTrue();
+		assertThat(matcher.matches(exchange).block().isMatch()).isTrue();
 	}
 
 	@Test
@@ -103,7 +103,7 @@ public class PathMatcherServerWebExchangeMatcherTests {
 		matcher = new PathMatcherServerWebExchangeMatcher(pattern, method);
 		matcher.setPathMatcher(pathMatcher);
 
-		assertThat(matcher.matches(exchange).isMatch()).isFalse();
+		assertThat(matcher.matches(exchange).block().isMatch()).isFalse();
 
 		verifyZeroInteractions(pathMatcher);
 	}

+ 6 - 6
webflux/src/test/java/org/springframework/security/web/server/util/matcher/ServerWebExchangeMatchersTests.java

@@ -39,34 +39,34 @@ public class ServerWebExchangeMatchersTests {
 
 	@Test
 	public void antMatchersWhenSingleAndSamePatternThenMatches() throws Exception {
-		assertThat(antMatchers("/").matches(exchange).isMatch()).isTrue();
+		assertThat(antMatchers("/").matches(exchange).block().isMatch()).isTrue();
 	}
 
 	@Test
 	public void antMatchersWhenSingleAndSamePatternAndMethodThenMatches() throws Exception {
-		assertThat(antMatchers(HttpMethod.GET, "/").matches(exchange).isMatch()).isTrue();
+		assertThat(antMatchers(HttpMethod.GET, "/").matches(exchange).block().isMatch()).isTrue();
 	}
 
 	@Test
 	public void antMatchersWhenSingleAndSamePatternAndDiffMethodThenDoesNotMatch() throws Exception {
-		assertThat(antMatchers(HttpMethod.POST, "/").matches(exchange).isMatch()).isFalse();
+		assertThat(antMatchers(HttpMethod.POST, "/").matches(exchange).block().isMatch()).isFalse();
 	}
 
 	@Test
 	public void antMatchersWhenSingleAndDifferentPatternThenDoesNotMatch() throws Exception {
-		assertThat(antMatchers("/foobar").matches(exchange).isMatch()).isFalse();
+		assertThat(antMatchers("/foobar").matches(exchange).block().isMatch()).isFalse();
 	}
 
 	@Test
 	public void antMatchersWhenMultiThenMatches() throws Exception {
-		assertThat(antMatchers("/foobar", "/").matches(exchange).isMatch()).isTrue();
+		assertThat(antMatchers("/foobar", "/").matches(exchange).block().isMatch()).isTrue();
 	}
 
 	@Test
 	public void anyExchangeWhenMockThenMatches() {
 		ServerWebExchange mockExchange = mock(ServerWebExchange.class);
 
-		assertThat(anyExchange().matches(mockExchange).isMatch()).isTrue();
+		assertThat(anyExchange().matches(mockExchange).block().isMatch()).isTrue();
 
 		verifyZeroInteractions(mockExchange);
 	}