1
0
mirror of synced 2026-08-04 17:27:13 +00:00

Fix WebTestClient Support

Fixes gh-4419
This commit is contained in:
Rob Winch
2017-07-13 20:47:09 -05:00
parent 67f80dfadc
commit 03f2d654ad
7 changed files with 414 additions and 174 deletions
@@ -18,46 +18,67 @@
package org.springframework.security.test.web.reactive.server;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.test.web.reactive.server.MockServerConfigurer;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import reactor.core.publisher.Mono;
import java.security.Principal;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.UnaryOperator;
/**
* Test utilities for working with Spring Security and
* {{@link org.springframework.test.web.reactive.server.WebTestClient}} using
* {{{@link org.springframework.test.web.reactive.server.ExchangeMutatorWebFilter}}}.
* {{@link org.springframework.test.web.reactive.server.WebTestClient.Builder#apply(WebTestClientConfigurer)}}.
*
* @author Rob Winch
* @since 5.0
*/
public class SecurityExchangeMutators {
public class SecurityMockServerConfigurers {
/**
* Sets up Spring Security's {@link WebTestClient} test support
* @return the MockServerConfigurer to use
*/
public static MockServerConfigurer springSecurity() {
return new MockServerConfigurer() {
public void beforeServerCreated(WebHttpHandlerBuilder builder) {
builder.filters( filters -> filters.add(0, new MutatorFilter()));
}
};
}
/**
* Updates the ServerWebExchange to use the provided Principal
*
* @param principal the principal to use.
* @return the {@link Function<ServerWebExchange, ServerWebExchange>}} to use
* @return the {@link WebTestClientConfigurer} to use
*/
public static Function<ServerWebExchange, ServerWebExchange> withPrincipal(Principal principal) {
return m -> m.mutate().principal(Mono.just(principal)).build();
public static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockPrincipal(Principal principal) {
return (T) new MutatorWebTestClientConfigurer(m -> m.mutate().principal(Mono.just(principal)).build());
}
/**
* Updates the ServerWebExchange to use the provided Authentication as the Principal
*
* @param authentication the Authentication to use.
* @return the {@link Function<ServerWebExchange, ServerWebExchange>}} to use
* @return the {@link WebTestClientConfigurer}} to use
*/
public static Function<ServerWebExchange, ServerWebExchange> withAuthentication(Authentication authentication) {
return withPrincipal(authentication);
public static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockAuthentication(Authentication authentication) {
return mockPrincipal(authentication);
}
/**
@@ -65,10 +86,10 @@ public class SecurityExchangeMutators {
* the Principal
*
* @param userDetails the UserDetails to use.
* @return the {@link Function<ServerWebExchange, ServerWebExchange>}} to use
* @return the {@link WebTestClientConfigurer} to use
*/
public static Function<ServerWebExchange, ServerWebExchange> withUser(UserDetails userDetails) {
return withAuthentication(new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()));
public static <T extends WebTestClientConfigurer & MockServerConfigurer> T mockUser(UserDetails userDetails) {
return mockAuthentication(new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()));
}
/**
@@ -76,10 +97,10 @@ public class SecurityExchangeMutators {
* the Principal. This uses a default username of "user", password of "password", and granted authorities of
* "ROLE_USER".
*
* @return the {@link Function<ServerWebExchange, ServerWebExchange>}} to use
* @return the {@link UserExchangeMutator} to use
*/
public static UserExchangeMutator withUser() {
return withUser("user");
public static UserExchangeMutator mockUser() {
return mockUser("user");
}
@@ -88,17 +109,17 @@ public class SecurityExchangeMutators {
* the Principal. This uses a default password of "password" and granted authorities of
* "ROLE_USER".
*
* @return the {@link Function<ServerWebExchange, ServerWebExchange>}} to use
* @return the {@link WebTestClientConfigurer} to use
*/
public static UserExchangeMutator withUser(String username) {
public static UserExchangeMutator mockUser(String username) {
return new UserExchangeMutator(username);
}
/**
* Updates the WebServerExchange using {@code SecurityExchangeMutators#withUser(UserDetails)}. Defaults to use a
* Updates the WebServerExchange using {@code {@link SecurityMockServerConfigurers#mockUser(UserDetails)}. Defaults to use a
* password of "password" and granted authorities of "ROLE_USER".
*/
public static class UserExchangeMutator implements Function<ServerWebExchange, ServerWebExchange> {
public static class UserExchangeMutator implements WebTestClientConfigurer, MockServerConfigurer {
private final User.UserBuilder userBuilder;
private UserExchangeMutator(String username) {
@@ -182,8 +203,73 @@ public class SecurityExchangeMutators {
}
@Override
public ServerWebExchange apply(ServerWebExchange serverWebExchange) {
return withUser(userBuilder.build()).apply(serverWebExchange);
public void beforeServerCreated(WebHttpHandlerBuilder builder) {
configurer().beforeServerCreated(builder);
}
@Override
public void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {
configurer().afterConfigureAdded(serverSpec);
}
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder, @Nullable WebHttpHandlerBuilder webHttpHandlerBuilder, @Nullable ClientHttpConnector clientHttpConnector) {
configurer().afterConfigurerAdded(builder, webHttpHandlerBuilder, clientHttpConnector);
}
private <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {
return mockUser(userBuilder.build());
}
}
private static class MutatorWebTestClientConfigurer implements WebTestClientConfigurer, MockServerConfigurer {
private final Function<ServerWebExchange, ServerWebExchange> mutator;
private MutatorWebTestClientConfigurer(Function<ServerWebExchange, ServerWebExchange> mutator) {
this.mutator = mutator;
}
@Override
public void beforeServerCreated(WebHttpHandlerBuilder builder) {
builder.filters(addSetupMutatorFilter());
}
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder, @Nullable WebHttpHandlerBuilder webHttpHandlerBuilder, @Nullable ClientHttpConnector clientHttpConnector) {
webHttpHandlerBuilder.filters(addSetupMutatorFilter());
}
private Consumer<List<WebFilter>> addSetupMutatorFilter() {
return filters -> filters.add(0, new SetupMutatorFilter(mutator));
}
}
private static class SetupMutatorFilter implements WebFilter {
private final Function<ServerWebExchange, ServerWebExchange> mutator;
private SetupMutatorFilter(Function<ServerWebExchange, ServerWebExchange> mutator) {
this.mutator = mutator;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain webFilterChain) {
exchange.getAttributes().computeIfAbsent(MutatorFilter.ATTRIBUTE_NAME, key -> mutator);
return webFilterChain.filter(exchange);
}
}
private static class MutatorFilter implements WebFilter {
public static final String ATTRIBUTE_NAME = "mutator";
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain webFilterChain) {
Function<ServerWebExchange, ServerWebExchange> mutator = exchange.getAttribute(ATTRIBUTE_NAME);
if(mutator != null) {
exchange.getAttributes().remove(ATTRIBUTE_NAME);
exchange = mutator.apply(exchange);
}
return webFilterChain.filter(exchange);
}
}
}