diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java index 29449209b6..52bfd977ac 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilter.java @@ -29,6 +29,7 @@ import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.stereotype.Controller; +import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.reactive.config.WebFluxConfigurer; @@ -43,6 +44,9 @@ class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { private static final Set> DEFAULT_INCLUDES; + private static final String[] OPTIONAL_INCLUDES = { + "org.springframework.security.config.web.server.ServerHttpSecurity" }; + static { Set> includes = new LinkedHashSet<>(); includes.add(ControllerAdvice.class); @@ -51,6 +55,13 @@ class WebFluxTypeExcludeFilter extends AnnotationCustomizableTypeExcludeFilter { includes.add(Converter.class); includes.add(GenericConverter.class); includes.add(WebExceptionHandler.class); + for (String optionalInclude : OPTIONAL_INCLUDES) { + try { + includes.add(ClassUtils.forName(optionalInclude, null)); + } + catch (Exception ex) { + } + } DEFAULT_INCLUDES = Collections.unmodifiableSet(includes); } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java index 16946190c6..2eaa216ce9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfiguration.java @@ -33,6 +33,8 @@ import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.reactive.server.MockServerConfigurer; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.server.WebHandler; @@ -47,6 +49,7 @@ import org.springframework.web.server.WebHandler; @Configuration @ConditionalOnClass({ WebClient.class, WebTestClient.class }) @AutoConfigureAfter({ CodecsAutoConfiguration.class, WebFluxAutoConfiguration.class }) +@Import(WebTestClientSecurityConfiguration.class) @EnableConfigurationProperties public class WebTestClientAutoConfiguration { @@ -54,9 +57,14 @@ public class WebTestClientAutoConfiguration { @ConditionalOnMissingBean @ConditionalOnBean(WebHandler.class) public WebTestClient webTestClient(ApplicationContext applicationContext, - List customizers) { - WebTestClient.Builder builder = WebTestClient - .bindToApplicationContext(applicationContext).configureClient(); + List customizers, + List configurers) { + WebTestClient.MockServerSpec mockServerSpec = WebTestClient + .bindToApplicationContext(applicationContext); + for (MockServerConfigurer configurer : configurers) { + mockServerSpec.apply(configurer); + } + WebTestClient.Builder builder = mockServerSpec.configureClient(); for (WebTestClientBuilderCustomizer customizer : customizers) { customizer.customize(builder); } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientSecurityConfiguration.java b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientSecurityConfiguration.java new file mode 100644 index 0000000000..ed33932de4 --- /dev/null +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientSecurityConfiguration.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.boot.test.autoconfigure.web.reactive; + +/** Configuration for Spring Security's {@link org.springframework.test.web.reactive.server.WebTestClient} integration. +* + * @author Madhura Bhave + */ + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; +import org.springframework.test.web.reactive.server.MockServerConfigurer; + +@Configuration +@ConditionalOnClass(SecurityMockServerConfigurers.class) +class WebTestClientSecurityConfiguration { + + @Bean + public MockServerConfigurer get() { + return SecurityMockServerConfigurers.springSecurity(); + } + +} diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories index fb1756d37b..cbc25286c2 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/main/resources/META-INF/spring.factories @@ -78,6 +78,8 @@ org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration # AutoConfigureWebClient auto-configuration imports org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient=\ +org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\ +org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\ org.springframework.boot.test.autoconfigure.web.reactive.WebTestClientAutoConfiguration # AutoConfigureWebFlux auto-configuration imports diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java index 8dbe16ae67..93d77fe0b9 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests.java @@ -30,6 +30,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.restdocs.mockmvc.MockMvcRestDocumentation; import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler; import org.springframework.restdocs.templates.TemplateFormats; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.FileSystemUtils; @@ -46,6 +47,7 @@ import static org.springframework.restdocs.webtestclient.WebTestClientRestDocume */ @RunWith(SpringRunner.class) @WebFluxTest +@WithMockUser @AutoConfigureRestDocs(uriScheme = "https", uriHost = "api.example.com", uriPort = 443) public class WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrationTests { @@ -59,8 +61,8 @@ public class WebTestClientRestDocsAutoConfigurationAdvancedConfigurationIntegrat @Test public void defaultSnippetsAreWritten() throws Exception { - this.webTestClient.get().uri("/").exchange().expectBody() - .consumeWith(document("default-snippets")); + this.webTestClient.get().uri("/").exchange().expectStatus().is2xxSuccessful() + .expectBody().consumeWith(document("default-snippets")); File defaultSnippetsDir = new File("target/generated-snippets/default-snippets"); assertThat(defaultSnippetsDir).exists(); assertThat(new File(defaultSnippetsDir, "curl-request.md")) diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationIntegrationTests.java index 0a10c84543..bbf8689b08 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/restdocs/WebTestClientRestDocsAutoConfigurationIntegrationTests.java @@ -25,6 +25,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.util.FileSystemUtils; @@ -39,6 +40,7 @@ import static org.springframework.restdocs.webtestclient.WebTestClientRestDocume */ @RunWith(SpringRunner.class) @WebFluxTest +@WithMockUser @AutoConfigureRestDocs(uriScheme = "https", uriHost = "api.example.com", uriPort = 443) public class WebTestClientRestDocsAutoConfigurationIntegrationTests { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilterTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilterTests.java index f3ab2b13ad..98b5f11227 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilterTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebFluxTypeExcludeFilterTests.java @@ -25,6 +25,7 @@ import org.springframework.context.annotation.FilterType; import org.springframework.core.type.classreading.MetadataReader; import org.springframework.core.type.classreading.MetadataReaderFactory; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; +import org.springframework.security.config.web.server.ServerHttpSecurity; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; @@ -53,6 +54,7 @@ public class WebFluxTypeExcludeFilterTests { assertThat(excludes(filter, ExampleWeb.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); + assertThat(excludes(filter, ExampleServerHttpSecurity.class)).isFalse(); } @Test @@ -65,6 +67,7 @@ public class WebFluxTypeExcludeFilterTests { assertThat(excludes(filter, ExampleWeb.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); + assertThat(excludes(filter, ExampleServerHttpSecurity.class)).isFalse(); } @Test @@ -77,6 +80,7 @@ public class WebFluxTypeExcludeFilterTests { assertThat(excludes(filter, ExampleWeb.class)).isTrue(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); + assertThat(excludes(filter, ExampleServerHttpSecurity.class)).isTrue(); } @Test @@ -89,6 +93,7 @@ public class WebFluxTypeExcludeFilterTests { assertThat(excludes(filter, ExampleWeb.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isFalse(); + assertThat(excludes(filter, ExampleServerHttpSecurity.class)).isFalse(); } @Test @@ -101,6 +106,7 @@ public class WebFluxTypeExcludeFilterTests { assertThat(excludes(filter, ExampleWeb.class)).isFalse(); assertThat(excludes(filter, ExampleService.class)).isTrue(); assertThat(excludes(filter, ExampleRepository.class)).isTrue(); + assertThat(excludes(filter, ExampleServerHttpSecurity.class)).isFalse(); } private boolean excludes(WebFluxTypeExcludeFilter filter, Class type) @@ -164,4 +170,8 @@ public class WebFluxTypeExcludeFilterTests { } + static class ExampleServerHttpSecurity extends ServerHttpSecurity { + + } + } diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java index 8d1029d995..3801dbb973 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/WebTestClientAutoConfigurationTests.java @@ -18,19 +18,24 @@ package org.springframework.boot.test.autoconfigure.web.reactive; import java.time.Duration; import java.time.temporal.ChronoUnit; +import java.util.List; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.codec.CodecConfigurer; +import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.reactive.server.WebTestClient; +import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebHandler; +import org.springframework.web.server.adapter.WebHttpHandlerBuilder; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -80,6 +85,41 @@ public class WebTestClientAutoConfigurationTests { }); } + @Test + @SuppressWarnings("unchecked") + public void shouldApplySpringSecurityConfigurer() { + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .run((context) -> { + WebTestClient webTestClient = context.getBean(WebTestClient.class); + WebTestClient.Builder builder = (WebTestClient.Builder) ReflectionTestUtils + .getField(webTestClient, "builder"); + WebHttpHandlerBuilder httpHandlerBuilder = (WebHttpHandlerBuilder) ReflectionTestUtils + .getField(builder, "httpHandlerBuilder"); + List filters = (List) ReflectionTestUtils + .getField(httpHandlerBuilder, "filters"); + assertThat(filters.get(0).getClass().getName()).isEqualTo( + "org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers$MutatorFilter"); + }); + } + + @Test + @SuppressWarnings("unchecked") + public void shouldNotApplySpringSecurityConfigurerWhenSpringSecurityNotOnClassPath() { + FilteredClassLoader classLoader = new FilteredClassLoader( + SecurityMockServerConfigurers.class); + this.contextRunner.withUserConfiguration(BaseConfiguration.class) + .withClassLoader(classLoader).run((context) -> { + WebTestClient webTestClient = context.getBean(WebTestClient.class); + WebTestClient.Builder builder = (WebTestClient.Builder) ReflectionTestUtils + .getField(webTestClient, "builder"); + WebHttpHandlerBuilder httpHandlerBuilder = (WebHttpHandlerBuilder) ReflectionTestUtils + .getField(builder, "httpHandlerBuilder"); + List filters = (List) ReflectionTestUtils + .getField(httpHandlerBuilder, "filters"); + assertThat(filters.size()).isEqualTo(0); + }); + } + @Configuration static class BaseConfiguration { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestAllControllersIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestAllControllersIntegrationTests.java index a87af480e3..d0dc7e9092 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestAllControllersIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestAllControllersIntegrationTests.java @@ -21,6 +21,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @@ -30,6 +31,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @author Stephane Nicoll */ @RunWith(SpringRunner.class) +@WithMockUser @WebFluxTest public class WebFluxTestAllControllersIntegrationTests { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestConverterIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestConverterIntegrationTests.java index 718e19adf7..f1efd012e1 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestConverterIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestConverterIntegrationTests.java @@ -23,6 +23,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @@ -32,6 +33,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @author Stephane Nicoll */ @RunWith(SpringRunner.class) +@WithMockUser @WebFluxTest(controllers = ExampleController2.class) public class WebFluxTestConverterIntegrationTests { diff --git a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestOneControllerIntegrationTests.java b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestOneControllerIntegrationTests.java index 49bacba591..6f65e3f8e1 100644 --- a/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestOneControllerIntegrationTests.java +++ b/spring-boot-project/spring-boot-test-autoconfigure/src/test/java/org/springframework/boot/test/autoconfigure/web/reactive/webclient/WebFluxTestOneControllerIntegrationTests.java @@ -21,6 +21,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.reactive.server.WebTestClient; @@ -30,6 +31,7 @@ import org.springframework.test.web.reactive.server.WebTestClient; * @author Stephane Nicoll */ @RunWith(SpringRunner.class) +@WithMockUser @WebFluxTest(controllers = ExampleController1.class) public class WebFluxTestOneControllerIntegrationTests {