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…
Reference in New Issue