diff --git a/spring-test/src/main/java/org/springframework/test/web/reactive/server/UserWebTestClientConfigurer.java b/spring-test/src/main/java/org/springframework/test/web/reactive/server/UserWebTestClientConfigurer.java new file mode 100644 index 00000000000..97e43d5a3a7 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/web/reactive/server/UserWebTestClientConfigurer.java @@ -0,0 +1,110 @@ +/* + * Copyright 2002-present the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.test.web.reactive.server; + +import java.security.cert.X509Certificate; + +import org.jspecify.annotations.Nullable; +import reactor.core.publisher.Mono; + +import org.springframework.http.client.reactive.ClientHttpConnector; +import org.springframework.http.server.reactive.SslInfo; +import org.springframework.util.Assert; +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; + +/** + * {@link WebTestClientConfigurer} that modifies WebFlux mock server requests + * by setting their {@link SslInfo}. + * + *

You can apply the configurer to a {@link WebTestClient.Builder}: + * + *

+ * WebTestClient client = webTestClientBuilder
+ *         .apply(UserWebTestClientConfigurer.x509(certificate))
+ *         .build();
+ * 
+ * + *

Or mutate an already built {@link WebTestClient}: + * + *

+ * WebTestClient newClient =
+ *         client.mutateWith(UserWebTestClientConfigurer.x509(certificate));
+ * 
+ * + *

Note: This configurer is applicable only to WebFlux mock + * server setup. For a {@code WebTestClient.Builder} with a liver server setup, + * or a non-WebFlux, mock server, {@link IllegalStateException} is raised. + * + *

For tests with a MockMvc server, refer to a similar facility to set the + * user identity per request through Spring Security's + * {@code SecurityMockMvcRequestPostProcessors}. + * + * @author Rossen Stoyanchev + * @since 7.0 + */ +public final class UserWebTestClientConfigurer implements WebTestClientConfigurer { + + private final SslInfo info; + + + private UserWebTestClientConfigurer(SslInfo info) { + this.info = info; + } + + + @Override + public void afterConfigurerAdded( + WebTestClient.Builder builder, @Nullable WebHttpHandlerBuilder httpHandlerBuilder, + @Nullable ClientHttpConnector connector) { + + Assert.state(httpHandlerBuilder != null, "This configurer is applicable only to a mock WebFlux server"); + httpHandlerBuilder.filters(filters -> filters.add(0, new UserWebFilter())); + } + + + /** + * Create a configurer with the given {@link X509Certificate X509 certificate(s)}. + */ + public static UserWebTestClientConfigurer x509(X509Certificate... certificates) { + return sslInfo(SslInfo.from("1", certificates)); + } + + /** + * Create a configurer with the given {@link SslInfo}. + */ + public static UserWebTestClientConfigurer sslInfo(SslInfo info) { + return new UserWebTestClientConfigurer(info); + } + + + private final class UserWebFilter implements WebFilter { + + @Override + public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { + + exchange = exchange.mutate() + .request(builder -> builder.sslInfo(UserWebTestClientConfigurer.this.info)) + .build(); + + return chain.filter(exchange); + } + } + +} diff --git a/spring-test/src/test/java/org/springframework/test/web/reactive/server/MockServerSpecTests.java b/spring-test/src/test/java/org/springframework/test/web/reactive/server/MockServerSpecTests.java index dc1a0ce5868..faffa388906 100644 --- a/spring-test/src/test/java/org/springframework/test/web/reactive/server/MockServerSpecTests.java +++ b/spring-test/src/test/java/org/springframework/test/web/reactive/server/MockServerSpecTests.java @@ -22,6 +22,7 @@ import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; +import org.springframework.cglib.core.internal.Function; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DefaultDataBufferFactory; import org.springframework.http.server.reactive.SslInfo; @@ -94,9 +95,25 @@ public class MockServerSpecTests { @Test void sslInfo() { + testSslInfo(info -> this.serverSpec.sslInfo(info).build()); + } + + @Test + void sslInfoViaWebTestClientConfigurer() { + testSslInfo(info -> this.serverSpec.configureClient().apply(UserWebTestClientConfigurer.sslInfo(info)).build()); + } + + @Test + void sslInfoViaMutate() { + testSslInfo(info -> this.serverSpec.build().mutateWith(UserWebTestClientConfigurer.sslInfo(info))); + } + + private void testSslInfo(Function function) { SslInfo info = SslInfo.from("123"); - this.serverSpec.sslInfo(info).build().get().uri("/").exchange().expectStatus().isOk(); - assertThat(this.serverSpec.getSavedSslInfo()).isSameAs(info); + function.apply(info).get().uri("/").exchange().expectStatus().isOk(); + + SslInfo actual = this.serverSpec.getSavedSslInfo(); + assertThat(actual).isSameAs(info); }