mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-30 22:28:46 +00:00 
			
		
		
		
	EnableWebFluxSecurityTests uses SpringTestRule
This will hopefully resolve the periodic failures in EnableWebFluxSecurityTests
This commit is contained in:
		
							parent
							
								
									9d46af3d7c
								
							
						
					
					
						commit
						d231441cc0
					
				| @ -16,9 +16,8 @@ | |||||||
| 
 | 
 | ||||||
| package org.springframework.security.config.annotation.web.reactive; | package org.springframework.security.config.annotation.web.reactive; | ||||||
| 
 | 
 | ||||||
|  | import org.junit.Rule; | ||||||
| import org.junit.Test; | 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.beans.factory.annotation.Autowired; | ||||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||||
| import org.springframework.core.Ordered; | 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.DataBuffer; | ||||||
| import org.springframework.core.io.buffer.DefaultDataBufferFactory; | import org.springframework.core.io.buffer.DefaultDataBufferFactory; | ||||||
| import org.springframework.security.authentication.TestingAuthenticationToken; | 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.config.web.server.ServerHttpSecurity; | ||||||
| import org.springframework.security.core.Authentication; | import org.springframework.security.core.Authentication; | ||||||
| import org.springframework.security.core.userdetails.MapReactiveUserDetailsService; | 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.ReactiveUserDetailsService; | ||||||
|  | import org.springframework.security.core.userdetails.User; | ||||||
| import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; | ||||||
| import org.springframework.security.crypto.password.PasswordEncoder; | import org.springframework.security.crypto.password.PasswordEncoder; | ||||||
| import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; | import org.springframework.security.test.web.reactive.server.WebTestClientBuilder; | ||||||
| import org.springframework.security.web.server.SecurityWebFilterChain; | import org.springframework.security.web.server.SecurityWebFilterChain; | ||||||
| import org.springframework.security.web.server.WebFilterChainProxy; | import org.springframework.security.web.server.WebFilterChainProxy; | ||||||
| import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher; | 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.FluxExchangeResult; | ||||||
| import org.springframework.test.web.reactive.server.WebTestClient; | import org.springframework.test.web.reactive.server.WebTestClient; | ||||||
| import org.springframework.util.LinkedMultiValueMap; | import org.springframework.util.LinkedMultiValueMap; | ||||||
| @ -56,228 +55,202 @@ import static org.springframework.web.reactive.function.client.ExchangeFilterFun | |||||||
|  * @author Rob Winch |  * @author Rob Winch | ||||||
|  * @since 5.0 |  * @since 5.0 | ||||||
|  */ |  */ | ||||||
| @RunWith(Enclosed.class) |  | ||||||
| public class EnableWebFluxSecurityTests { | public class EnableWebFluxSecurityTests { | ||||||
| 	@RunWith(SpringRunner.class) | 	@Rule | ||||||
| 	public static class Defaults { | 	public final SpringTestRule spring = new SpringTestRule(); | ||||||
| 		@Autowired WebFilterChainProxy springSecurityFilterChain; |  | ||||||
| 
 | 
 | ||||||
| 		@Test | 	@Autowired | ||||||
| 		public void defaultRequiresAuthentication() { | 	WebFilterChainProxy springSecurityFilterChain; | ||||||
| 			WebTestClient client = WebTestClientBuilder |  | ||||||
| 				.bindToWebFilters(this.springSecurityFilterChain) |  | ||||||
| 				.build(); |  | ||||||
| 
 | 
 | ||||||
| 			client.get() | 	@Test | ||||||
| 				.uri("/") | 	public void defaultRequiresAuthentication() { | ||||||
| 				.exchange() | 		this.spring.register(Config.class).autowire(); | ||||||
| 				.expectStatus().isUnauthorized() |  | ||||||
| 				.expectBody().isEmpty(); |  | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		@Test | 		WebTestClient client = WebTestClientBuilder | ||||||
| 		public void authenticateWhenBasicThenNoSession() { | 			.bindToWebFilters(this.springSecurityFilterChain) | ||||||
| 			WebTestClient client = WebTestClientBuilder | 			.build(); | ||||||
| 				.bindToWebFilters(this.springSecurityFilterChain) |  | ||||||
| 				.filter(basicAuthentication()) |  | ||||||
| 				.build(); |  | ||||||
| 
 | 
 | ||||||
| 			FluxExchangeResult<String> result = client.get() | 		client.get() | ||||||
| 				.attributes(basicAuthenticationCredentials("user", "password")).exchange() | 			.uri("/") | ||||||
| 				.expectStatus() | 			.exchange() | ||||||
| 				.isOk() | 			.expectStatus().isUnauthorized() | ||||||
| 				.returnResult(String.class); | 			.expectBody().isEmpty(); | ||||||
| 			result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty())); | 	} | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		@Test | 	@Test | ||||||
| 		public void defaultPopulatesReactorContext() { | 	public void authenticateWhenBasicThenNoSession() { | ||||||
| 			Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER"); | 		this.spring.register(Config.class).autowire(); | ||||||
| 			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 | 		WebTestClient client = WebTestClientBuilder | ||||||
| 				.get() | 			.bindToWebFilters(this.springSecurityFilterChain) | ||||||
| 				.uri("/") |  | ||||||
| 				.exchange() |  | ||||||
| 				.expectStatus().isOk() |  | ||||||
| 				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo(currentPrincipal.getName())); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		@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()) | 			.filter(basicAuthentication()) | ||||||
| 			.build(); | 			.build(); | ||||||
| 
 | 
 | ||||||
| 			client | 		FluxExchangeResult<String> result = client.get() | ||||||
| 				.get() | 			.attributes(basicAuthenticationCredentials("user", "password")).exchange() | ||||||
| 				.uri("/") | 			.expectStatus() | ||||||
| 				.attributes(basicAuthenticationCredentials("user","password")) | 			.isOk() | ||||||
| 				.exchange() | 			.returnResult(String.class); | ||||||
| 				.expectStatus().isOk() | 		result.assertWithDiagnostics(() -> assertThat(result.getResponseCookies().isEmpty())); | ||||||
| 				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); | 	} | ||||||
| 		} |  | ||||||
| 
 | 
 | ||||||
| 		@EnableWebFluxSecurity | 	@Test | ||||||
| 		static class Config { | 	public void defaultPopulatesReactorContext() { | ||||||
| 			@Bean | 		this.spring.register(Config.class).autowire(); | ||||||
| 			public ReactiveUserDetailsService userDetailsRepository() { | 		Principal currentPrincipal = new TestingAuthenticationToken("user", "password", "ROLE_USER"); | ||||||
| 				return new MapReactiveUserDetailsService(User.withUsername("user") | 		WebTestClient client = WebTestClientBuilder.bindToWebFilters( | ||||||
| 					.password("password") | 			(exchange, chain) -> | ||||||
| 					.roles("USER") | 				chain.filter(exchange.mutate().principal(Mono.just(currentPrincipal)).build()), | ||||||
| 					.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")); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	@EnableWebFluxSecurity | ||||||
|  | 	static class Config { | ||||||
|  | 		@Bean | ||||||
|  | 		public ReactiveUserDetailsService userDetailsRepository() { | ||||||
|  | 			return new MapReactiveUserDetailsService(User.withUsername("user") | ||||||
|  | 				.password("password") | ||||||
|  | 				.roles("USER") | ||||||
|  | 				.build() | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@RunWith(SpringRunner.class) | 	@Test | ||||||
| 	public static class CustomPasswordEncoder { | 	public void passwordEncoderBeanIsUsed() { | ||||||
| 		@Autowired WebFilterChainProxy springSecurityFilterChain; | 		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(); | ||||||
| 
 | 
 | ||||||
| 		@Test | 		client | ||||||
| 		public void passwordEncoderBeanIsUsed() { | 			.get() | ||||||
| 			WebTestClient client = WebTestClientBuilder.bindToWebFilters( | 			.uri("/") | ||||||
| 				this.springSecurityFilterChain, | 			.attributes(basicAuthenticationCredentials("user","password")) | ||||||
| 				(exchange,chain) -> | 			.exchange() | ||||||
| 					Mono.subscriberContext() | 			.expectStatus().isOk() | ||||||
| 						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class)) | 			.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); | ||||||
| 						.flatMap( principal -> exchange.getResponse() | 	} | ||||||
| 							.writeWith(Mono.just(toDataBuffer(principal.getName())))) |  | ||||||
| 			) |  | ||||||
| 			.filter(basicAuthentication()) |  | ||||||
| 			.build(); |  | ||||||
| 
 | 
 | ||||||
| 			client | 	@EnableWebFluxSecurity | ||||||
| 				.get() | 	static class CustomPasswordEncoderConfig { | ||||||
| 				.uri("/") | 		@Bean | ||||||
| 				.attributes(basicAuthenticationCredentials("user","password")) | 		public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) { | ||||||
| 				.exchange() | 			return new MapReactiveUserDetailsService(User.withUsername("user") | ||||||
| 				.expectStatus().isOk() | 				.password(encoder.encode("password")) | ||||||
| 				.expectBody(String.class).consumeWith( result -> assertThat(result.getResponseBody()).isEqualTo("user")); | 				.roles("USER") | ||||||
|  | 				.build() | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@EnableWebFluxSecurity | 		@Bean | ||||||
| 		static class Config { | 		public static PasswordEncoder passwordEncoder() { | ||||||
| 			@Bean | 			return new BCryptPasswordEncoder(); | ||||||
| 			public ReactiveUserDetailsService userDetailsRepository(PasswordEncoder encoder) { |  | ||||||
| 				return new MapReactiveUserDetailsService(User.withUsername("user") |  | ||||||
| 					.password(encoder.encode("password")) |  | ||||||
| 					.roles("USER") |  | ||||||
| 					.build() |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			@Bean |  | ||||||
| 			public static PasswordEncoder passwordEncoder() { |  | ||||||
| 				return new BCryptPasswordEncoder(); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 
 | 	@Test | ||||||
| 	@RunWith(SpringRunner.class) | 	public void formLoginWorks() { | ||||||
| 	public static class FormLoginTests { | 		this.spring.register(Config.class).autowire(); | ||||||
| 		@Autowired WebFilterChainProxy springSecurityFilterChain; | 		WebTestClient client = WebTestClientBuilder.bindToWebFilters( | ||||||
| 		@Test | 			this.springSecurityFilterChain, | ||||||
| 		public void formLoginWorks() { | 			(exchange,chain) -> | ||||||
| 			WebTestClient client = WebTestClientBuilder.bindToWebFilters( | 				Mono.subscriberContext() | ||||||
| 				this.springSecurityFilterChain, | 					.flatMap( c -> c.<Mono<Principal>>get(Authentication.class)) | ||||||
| 				(exchange,chain) -> | 					.flatMap( principal -> exchange.getResponse() | ||||||
| 					Mono.subscriberContext() | 						.writeWith(Mono.just(toDataBuffer(principal.getName())))) | ||||||
| 						.flatMap( c -> c.<Mono<Principal>>get(Authentication.class)) | 		) | ||||||
| 						.flatMap( principal -> exchange.getResponse() | 		.build(); | ||||||
| 							.writeWith(Mono.just(toDataBuffer(principal.getName())))) |  | ||||||
| 			) |  | ||||||
| 			.build(); |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 			MultiValueMap<String, String> data = new LinkedMultiValueMap<>(); | 		MultiValueMap<String, String> data = new LinkedMultiValueMap<>(); | ||||||
| 			data.add("username", "user"); | 		data.add("username", "user"); | ||||||
| 			data.add("password", "password"); | 		data.add("password", "password"); | ||||||
| 			client | 		client | ||||||
| 				.post() | 			.post() | ||||||
| 				.uri("/login") | 			.uri("/login") | ||||||
| 				.body(BodyInserters.fromFormData(data)) | 			.body(BodyInserters.fromFormData(data)) | ||||||
| 				.exchange() | 			.exchange() | ||||||
| 				.expectStatus().is3xxRedirection() | 			.expectStatus().is3xxRedirection() | ||||||
| 				.expectHeader().valueMatches("Location", "/"); | 			.expectHeader().valueMatches("Location", "/"); | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		@EnableWebFluxSecurity |  | ||||||
| 		static class Config { |  | ||||||
| 			@Bean |  | ||||||
| 			public ReactiveUserDetailsService userDetailsRepository() { |  | ||||||
| 				return new MapReactiveUserDetailsService(User.withUsername("user") |  | ||||||
| 					.password("password") |  | ||||||
| 					.roles("USER") |  | ||||||
| 					.build() |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	@RunWith(SpringRunner.class) | 	@Test | ||||||
| 	public static class MultiServerHttpSecurity { | 	public void multiWorks() { | ||||||
| 		@Autowired WebFilterChainProxy springSecurityFilterChain; | 		this.spring.register(MultiSecurityHttpConfig.class).autowire(); | ||||||
|  | 		WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build(); | ||||||
| 
 | 
 | ||||||
| 		@Test | 		client.get() | ||||||
| 		public void multiWorks() { | 			.uri("/api/test") | ||||||
| 			WebTestClient client = WebTestClientBuilder.bindToWebFilters(this.springSecurityFilterChain).build(); | 			.exchange() | ||||||
|  | 			.expectStatus().isUnauthorized() | ||||||
|  | 			.expectBody().isEmpty(); | ||||||
| 
 | 
 | ||||||
| 			client.get() | 		client.get() | ||||||
| 				.uri("/api/test") | 			.uri("/test") | ||||||
| 				.exchange() | 			.exchange() | ||||||
| 				.expectStatus().isUnauthorized() | 			.expectStatus().isOk(); | ||||||
| 				.expectBody().isEmpty(); | 	} | ||||||
| 
 | 
 | ||||||
| 			client.get() | 	@EnableWebFluxSecurity | ||||||
| 				.uri("/test") | 	static class MultiSecurityHttpConfig { | ||||||
| 				.exchange() | 		@Order(Ordered.HIGHEST_PRECEDENCE) @Bean public SecurityWebFilterChain apiHttpSecurity( | ||||||
| 				.expectStatus().isOk(); | 			ServerHttpSecurity http) { | ||||||
|  | 			http.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) | ||||||
|  | 				.authorizeExchange().anyExchange().denyAll(); | ||||||
|  | 			return http.build(); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		@EnableWebFluxSecurity | 		@Bean public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) { | ||||||
| 		static class Config { | 			return http.build(); | ||||||
| 			@Order(Ordered.HIGHEST_PRECEDENCE) | 		} | ||||||
| 			@Bean |  | ||||||
| 			public SecurityWebFilterChain apiHttpSecurity(ServerHttpSecurity http) { |  | ||||||
| 				http |  | ||||||
| 					.securityMatcher(new PathPatternParserServerWebExchangeMatcher("/api/**")) |  | ||||||
| 					.authorizeExchange() |  | ||||||
| 						.anyExchange().denyAll(); |  | ||||||
| 				return http.build(); |  | ||||||
| 			} |  | ||||||
| 
 | 
 | ||||||
| 			@Bean | 		@Bean public ReactiveUserDetailsService userDetailsRepository() { | ||||||
| 			public SecurityWebFilterChain httpSecurity(ServerHttpSecurity http) { | 			return new MapReactiveUserDetailsService( | ||||||
| 				return http.build(); | 				User.withUsername("user").password("password").roles("USER").build()); | ||||||
| 			} |  | ||||||
| 
 |  | ||||||
| 			@Bean |  | ||||||
| 			public ReactiveUserDetailsService userDetailsRepository() { |  | ||||||
| 				return new MapReactiveUserDetailsService(User.withUsername("user") |  | ||||||
| 					.password("password") |  | ||||||
| 					.roles("USER") |  | ||||||
| 					.build() |  | ||||||
| 				); |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user