diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java index 94242e48460..9ae39f8b5d4 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java @@ -19,6 +19,7 @@ import java.util.Arrays; import java.util.List; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.context.ApplicationContext; @@ -33,6 +34,7 @@ import org.springframework.security.web.server.WebFilterChainProxy; import org.springframework.security.web.server.WebFilterChainProxy.DefaultWebFilterChainDecorator; import org.springframework.security.web.server.WebFilterChainProxy.WebFilterChainDecorator; +import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.web.reactive.result.view.AbstractView; @@ -79,10 +81,11 @@ void setFilterChainPostProcessor(ObjectPostProcessor po @Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME) @Order(WEB_FILTER_CHAIN_FILTER_ORDER) - WebFilterChainProxy springSecurityWebFilterChainFilter() { + WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider firewall) { WebFilterChainProxy proxy = new WebFilterChainProxy(getSecurityWebFilterChains()); WebFilterChainDecorator decorator = this.postProcessor.postProcess(new DefaultWebFilterChainDecorator()); proxy.setFilterChainDecorator(decorator); + firewall.ifUnique(proxy::setFirewall); return proxy; } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java index 8893efcd330..4c8c249d7eb 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java @@ -16,14 +16,24 @@ package org.springframework.security.config.annotation.web.reactive; +import java.util.Collections; + +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import reactor.core.publisher.Mono; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; +import org.springframework.mock.http.server.reactive.MockServerHttpRequest; +import org.springframework.mock.web.server.MockServerWebExchange; import org.springframework.security.config.test.SpringTestContext; import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration; import org.springframework.security.web.server.WebFilterChainProxy; +import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall; +import org.springframework.web.server.handler.DefaultWebFilterChain; import static org.assertj.core.api.Assertions.assertThat; @@ -47,6 +57,32 @@ public void loadConfigWhenReactiveUserDetailsServiceConfiguredThenWebFilterChain assertThat(webFilterChainProxy).isNotNull(); } + @Test + void loadConfigWhenDefaultThenFirewalled() throws Exception { + this.spring + .register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class) + .autowire(); + WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); + DefaultWebFilterChain chain = emptyChain(); + webFilterChainProxy.filter(exchange, chain).block(); + assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + } + + @Test + void loadConfigWhenFirewallBeanThenCustomized() throws Exception { + this.spring + .register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class, + WebFluxSecurityConfiguration.class, NoOpFirewallConfig.class) + .autowire(); + WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class); + MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build()); + DefaultWebFilterChain chain = emptyChain(); + webFilterChainProxy.filter(exchange, chain).block(); + assertThat(exchange.getResponse().getStatusCode()).isNotEqualTo(HttpStatus.BAD_REQUEST); + } + @Test public void loadConfigWhenBeanProxyingEnabledAndSubclassThenWebFilterChainProxyExists() { this.spring @@ -57,6 +93,20 @@ public void loadConfigWhenBeanProxyingEnabledAndSubclassThenWebFilterChainProxyE assertThat(webFilterChainProxy).isNotNull(); } + private static @NotNull DefaultWebFilterChain emptyChain() { + return new DefaultWebFilterChain((webExchange) -> Mono.empty(), Collections.emptyList()); + } + + @Configuration + static class NoOpFirewallConfig { + + @Bean + ServerWebExchangeFirewall noOpFirewall() { + return ServerWebExchangeFirewall.INSECURE_NOOP; + } + + } + @Configuration static class SubclassConfig extends WebFluxSecurityConfiguration {