EnableWebFluxSecurityTests uses SpringTestRule

This will hopefully resolve the periodic failures in
EnableWebFluxSecurityTests
This commit is contained in:
Rob Winch 2017-10-18 15:07:27 -05:00
parent 9d46af3d7c
commit d231441cc0
1 changed files with 168 additions and 195 deletions

View File

@ -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()
);
}
} }
} }