Browse Source

EnableWebFluxSecurityTests uses SpringTestRule

This will hopefully resolve the periodic failures in
EnableWebFluxSecurityTests
Rob Winch 7 năm trước cách đây
mục cha
commit
d231441cc0

+ 171 - 198
config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java

@@ -16,9 +16,8 @@
 
 package org.springframework.security.config.annotation.web.reactive;
 
+import org.junit.Rule;
 import org.junit.Test;
-import org.junit.experimental.runners.Enclosed;
-import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.core.Ordered;
@@ -26,18 +25,18 @@ import org.springframework.core.annotation.Order;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.core.io.buffer.DefaultDataBufferFactory;
 import org.springframework.security.authentication.TestingAuthenticationToken;
+import org.springframework.security.config.test.SpringTestRule;
 import org.springframework.security.config.web.server.ServerHttpSecurity;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
-import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
+import org.springframework.security.core.userdetails.User;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.security.crypto.password.PasswordEncoder;
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
 import org.springframework.security.web.server.SecurityWebFilterChain;
 import org.springframework.security.web.server.WebFilterChainProxy;
 import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
-import org.springframework.test.context.junit4.SpringRunner;
 import org.springframework.test.web.reactive.server.FluxExchangeResult;
 import org.springframework.test.web.reactive.server.WebTestClient;
 import org.springframework.util.LinkedMultiValueMap;
@@ -56,228 +55,202 @@ import static org.springframework.web.reactive.function.client.ExchangeFilterFun
  * @author Rob Winch
  * @since 5.0
  */
-@RunWith(Enclosed.class)
 public class EnableWebFluxSecurityTests {
-	@RunWith(SpringRunner.class)
-	public static class Defaults {
-		@Autowired WebFilterChainProxy springSecurityFilterChain;
-
-		@Test
-		public void defaultRequiresAuthentication() {
-			WebTestClient client = WebTestClientBuilder
-				.bindToWebFilters(this.springSecurityFilterChain)
-				.build();
-
-			client.get()
-				.uri("/")
-				.exchange()
-				.expectStatus().isUnauthorized()
-				.expectBody().isEmpty();
-		}
-
-		@Test
-		public void authenticateWhenBasicThenNoSession() {
-			WebTestClient client = WebTestClientBuilder
-				.bindToWebFilters(this.springSecurityFilterChain)
-				.filter(basicAuthentication())
-				.build();
-
-			FluxExchangeResult<String> result = client.get()
-				.attributes(basicAuthenticationCredentials("user", "password")).exchange()
-				.expectStatus()
-				.isOk()
-				.returnResult(String.class);
-			result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty()));
-		}
+	@Rule
+	public final SpringTestRule spring = new SpringTestRule();
 
-		@Test
-		public void defaultPopulatesReactorContext() {
-			Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER");
-			WebTestClient client = WebTestClientBuilder.bindToWebFilters(
-				(exchange, chain) ->
-					chain.filter(exchange.mutate().principal(Mono.just(currentPrincipal)).build()),
-				this.springSecurityFilterChain,
-				(exchange,chain) ->
-					Mono.subscriberContext()
-						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
-						.flatMap( principal -> exchange.getResponse()
-							.writeWith(Mono.just(toDataBuffer(principal.getName()))))
-			).build();
+	@Autowired
+	WebFilterChainProxy springSecurityFilterChain;
 
-			client
-				.get()
-				.uri("/")
-				.exchange()
-				.expectStatus().isOk()
-				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName()));
-		}
+	@Test
+	public void defaultRequiresAuthentication() {
+		this.spring.register(Config.class).autowire();
 
-		@Test
-		public void defaultPopulatesReactorContextWhenAuthenticating() {
-			WebTestClient client = WebTestClientBuilder.bindToWebFilters(
-				this.springSecurityFilterChain,
-				(exchange,chain) ->
-					Mono.subscriberContext()
-						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
-						.flatMap( principal -> exchange.getResponse()
-							.writeWith(Mono.just(toDataBuffer(principal.getName()))))
-			)
-			.filter(basicAuthentication())
+		WebTestClient client = WebTestClientBuilder
+			.bindToWebFilters(this.springSecurityFilterChain)
 			.build();
 
-			client
-				.get()
-				.uri("/")
-				.attributes(basicAuthenticationCredentials("user","password"))
-				.exchange()
-				.expectStatus().isOk()
-				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user"));
-		}
-
-		@EnableWebFluxSecurity
-		static class Config {
-			@Bean
-			public ReactiveUserDetailsService userDetailsRepository() {
-				return new MapReactiveUserDetailsService(User.withUsername("user")
-					.password("password")
-					.roles("USER")
-					.build()
-				);
-			}
-		}
+		client.get()
+			.uri("/")
+			.exchange()
+			.expectStatus().isUnauthorized()
+			.expectBody().isEmpty();
 	}
 
-	@RunWith(SpringRunner.class)
-	public static class CustomPasswordEncoder {
-		@Autowired WebFilterChainProxy springSecurityFilterChain;
+	@Test
+	public void authenticateWhenBasicThenNoSession() {
+		this.spring.register(Config.class).autowire();
 
-		@Test
-		public void passwordEncoderBeanIsUsed() {
-			WebTestClient client = WebTestClientBuilder.bindToWebFilters(
-				this.springSecurityFilterChain,
-				(exchange,chain) ->
-					Mono.subscriberContext()
-						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
-						.flatMap( principal -> exchange.getResponse()
-							.writeWith(Mono.just(toDataBuffer(principal.getName()))))
-			)
+		WebTestClient client = WebTestClientBuilder
+			.bindToWebFilters(this.springSecurityFilterChain)
 			.filter(basicAuthentication())
 			.build();
 
-			client
-				.get()
-				.uri("/")
-				.attributes(basicAuthenticationCredentials("user","password"))
-				.exchange()
-				.expectStatus().isOk()
-				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user"));
-		}
-
-		@EnableWebFluxSecurity
-		static class Config {
-			@Bean
-			public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) {
-				return new MapReactiveUserDetailsService(User.withUsername("user")
-					.password(encoder.encode("password"))
-					.roles("USER")
-					.build()
-				);
-			}
+		FluxExchangeResult<String> result = client.get()
+			.attributes(basicAuthenticationCredentials("user", "password")).exchange()
+			.expectStatus()
+			.isOk()
+			.returnResult(String.class);
+		result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty()));
+	}
 
-			@Bean
-			public static PasswordEncoder passwordEncoder() {
-				return new BCryptPasswordEncoder();
-			}
-		}
+	@Test
+	public void defaultPopulatesReactorContext() {
+		this.spring.register(Config.class).autowire();
+		Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER");
+		WebTestClient client = WebTestClientBuilder.bindToWebFilters(
+			(exchange, chain) ->
+				chain.filter(exchange.mutate().principal(Mono.just(currentPrincipal)).build()),
+			this.springSecurityFilterChain,
+			(exchange,chain) ->
+				Mono.subscriberContext()
+					.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
+					.flatMap( principal -> exchange.getResponse()
+						.writeWith(Mono.just(toDataBuffer(principal.getName()))))
+		).build();
+
+		client
+			.get()
+			.uri("/")
+			.exchange()
+			.expectStatus().isOk()
+			.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName()));
 	}
 
+	@Test
+	public void defaultPopulatesReactorContextWhenAuthenticating() {
+		this.spring.register(Config.class).autowire();
+		WebTestClient client = WebTestClientBuilder.bindToWebFilters(
+			this.springSecurityFilterChain,
+			(exchange,chain) ->
+				Mono.subscriberContext()
+					.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
+					.flatMap( principal -> exchange.getResponse()
+						.writeWith(Mono.just(toDataBuffer(principal.getName()))))
+		)
+		.filter(basicAuthentication())
+		.build();
+
+		client
+			.get()
+			.uri("/")
+			.attributes(basicAuthenticationCredentials("user","password"))
+			.exchange()
+			.expectStatus().isOk()
+			.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user"));
+	}
 
-	@RunWith(SpringRunner.class)
-	public static class FormLoginTests {
-		@Autowired WebFilterChainProxy springSecurityFilterChain;
-		@Test
-		public void formLoginWorks() {
-			WebTestClient client = WebTestClientBuilder.bindToWebFilters(
-				this.springSecurityFilterChain,
-				(exchange,chain) ->
-					Mono.subscriberContext()
-						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
-						.flatMap( principal -> exchange.getResponse()
-							.writeWith(Mono.just(toDataBuffer(principal.getName()))))
-			)
-			.build();
+	@EnableWebFluxSecurity
+	static class Config {
+		@Bean
+		public ReactiveUserDetailsService userDetailsRepository() {
+			return new MapReactiveUserDetailsService(User.withUsername("user")
+				.password("password")
+				.roles("USER")
+				.build()
+			);
+		}
+	}
 
+	@Test
+	public void passwordEncoderBeanIsUsed() {
+		this.spring.register(CustomPasswordEncoderConfig.class).autowire();
+		WebTestClient client = WebTestClientBuilder.bindToWebFilters(
+			this.springSecurityFilterChain,
+			(exchange,chain) ->
+				Mono.subscriberContext()
+					.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
+					.flatMap( principal -> exchange.getResponse()
+						.writeWith(Mono.just(toDataBuffer(principal.getName()))))
+		)
+		.filter(basicAuthentication())
+		.build();
+
+		client
+			.get()
+			.uri("/")
+			.attributes(basicAuthenticationCredentials("user","password"))
+			.exchange()
+			.expectStatus().isOk()
+			.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user"));
+	}
 
-			MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
-			data.add("username", "user");
-			data.add("password", "password");
-			client
-				.post()
-				.uri("/login")
-				.body(BodyInserters.fromFormData(data))
-				.exchange()
-				.expectStatus().is3xxRedirection()
-				.expectHeader().valueMatches("Location", "/");
+	@EnableWebFluxSecurity
+	static class CustomPasswordEncoderConfig {
+		@Bean
+		public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) {
+			return new MapReactiveUserDetailsService(User.withUsername("user")
+				.password(encoder.encode("password"))
+				.roles("USER")
+				.build()
+			);
 		}
 
-		@EnableWebFluxSecurity
-		static class Config {
-			@Bean
-			public ReactiveUserDetailsService userDetailsRepository() {
-				return new MapReactiveUserDetailsService(User.withUsername("user")
-					.password("password")
-					.roles("USER")
-					.build()
-				);
-			}
+		@Bean
+		public static PasswordEncoder passwordEncoder() {
+			return new BCryptPasswordEncoder();
 		}
 	}
 
-	@RunWith(SpringRunner.class)
-	public static class MultiServerHttpSecurity {
-		@Autowired WebFilterChainProxy springSecurityFilterChain;
-
-		@Test
-		public void multiWorks() {
-			WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build();
+	@Test
+	public void formLoginWorks() {
+		this.spring.register(Config.class).autowire();
+		WebTestClient client = WebTestClientBuilder.bindToWebFilters(
+			this.springSecurityFilterChain,
+			(exchange,chain) ->
+				Mono.subscriberContext()
+					.flatMap( c -> c.<Mono<Principal>>get(Authentication.class))
+					.flatMap( principal -> exchange.getResponse()
+						.writeWith(Mono.just(toDataBuffer(principal.getName()))))
+		)
+		.build();
+
+
+		MultiValueMap<String, String> data = new LinkedMultiValueMap<>();
+		data.add("username", "user");
+		data.add("password", "password");
+		client
+			.post()
+			.uri("/login")
+			.body(BodyInserters.fromFormData(data))
+			.exchange()
+			.expectStatus().is3xxRedirection()
+			.expectHeader().valueMatches("Location", "/");
+	}
 
-			client.get()
-				.uri("/api/test")
-				.exchange()
-				.expectStatus().isUnauthorized()
-				.expectBody().isEmpty();
+	@Test
+	public void multiWorks() {
+		this.spring.register(MultiSecurityHttpConfig.class).autowire();
+		WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build();
+
+		client.get()
+			.uri("/api/test")
+			.exchange()
+			.expectStatus().isUnauthorized()
+			.expectBody().isEmpty();
+
+		client.get()
+			.uri("/test")
+			.exchange()
+			.expectStatus().isOk();
+	}
 
-			client.get()
-				.uri("/test")
-				.exchange()
-				.expectStatus().isOk();
+	@EnableWebFluxSecurity
+	static class MultiSecurityHttpConfig {
+		@Order(Ordered.HIGHEST_PRECEDENCE) @Bean public SecurityWebFilterChain apiHttpSecurity(
+			ServerHttpSecurity http) {
+			http.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**"))
+				.authorizeExchange().anyExchange().denyAll();
+			return http.build();
 		}
 
-		@EnableWebFluxSecurity
-		static class Config {
-			@Order(Ordered.HIGHEST_PRECEDENCE)
-			@Bean
-			public SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) {
-				http
-					.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**"))
-					.authorizeExchange()
-						.anyExchange().denyAll();
-				return http.build();
-			}
-
-			@Bean
-			public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) {
-				return http.build();
-			}
+		@Bean public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) {
+			return http.build();
+		}
 
-			@Bean
-			public ReactiveUserDetailsService userDetailsRepository() {
-				return new MapReactiveUserDetailsService(User.withUsername("user")
-					.password("password")
-					.roles("USER")
-					.build()
-				);
-			}
+		@Bean public ReactiveUserDetailsService userDetailsRepository() {
+			return new MapReactiveUserDetailsService(
+				User.withUsername("user").password("password").roles("USER").build());
 		}
 	}