Skip to content

Commit e86d88d

Browse files
committed
Support ServerExchangeRejectedHandler @bean
Closes gh-15975
1 parent e48d6b0 commit e86d88d

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.security.web.reactive.result.view.CsrfRequestDataValueProcessor;
3232
import org.springframework.security.web.server.SecurityWebFilterChain;
3333
import org.springframework.security.web.server.WebFilterChainProxy;
34+
import org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;
3435
import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;
3536
import org.springframework.util.ClassUtils;
3637
import org.springframework.util.ObjectUtils;
@@ -67,9 +68,11 @@ void setSecurityWebFilterChains(List<SecurityWebFilterChain> securityWebFilterCh
6768

6869
@Bean(SPRING_SECURITY_WEBFILTERCHAINFILTER_BEAN_NAME)
6970
@Order(WEB_FILTER_CHAIN_FILTER_ORDER)
70-
WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider<ServerWebExchangeFirewall> firewall) {
71+
WebFilterChainProxy springSecurityWebFilterChainFilter(ObjectProvider<ServerWebExchangeFirewall> firewall,
72+
ObjectProvider<ServerExchangeRejectedHandler> rejectedHandler) {
7173
WebFilterChainProxy webFilterChainProxy = new WebFilterChainProxy(getSecurityWebFilterChains());
7274
firewall.ifUnique(webFilterChainProxy::setFirewall);
75+
rejectedHandler.ifUnique(webFilterChainProxy::setExchangeRejectedHandler);
7376
return webFilterChainProxy;
7477
}
7578

config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
import org.springframework.security.config.test.SpringTestContextExtension;
3333
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
3434
import org.springframework.security.web.server.WebFilterChainProxy;
35+
import org.springframework.security.web.server.firewall.HttpStatusExchangeRejectedHandler;
36+
import org.springframework.security.web.server.firewall.ServerExchangeRejectedHandler;
3537
import org.springframework.security.web.server.firewall.ServerWebExchangeFirewall;
3638
import org.springframework.web.server.handler.DefaultWebFilterChain;
3739

@@ -66,6 +68,18 @@ void loadConfigWhenDefaultThenFirewalled() throws Exception {
6668
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
6769
}
6870

71+
@Test
72+
void loadConfigWhenCustomRejectedHandler() throws Exception {
73+
this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,
74+
WebFluxSecurityConfiguration.class, CustomServerExchangeRejectedHandlerConfig.class).autowire();
75+
WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);
76+
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build());
77+
DefaultWebFilterChain chain = emptyChain();
78+
webFilterChainProxy.filter(exchange, chain).block();
79+
assertThat(exchange.getResponse().getStatusCode())
80+
.isEqualTo(CustomServerExchangeRejectedHandlerConfig.EXPECTED_STATUS);
81+
}
82+
6983
@Test
7084
void loadConfigWhenFirewallBeanThenCustomized() throws Exception {
7185
this.spring.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,
@@ -99,6 +113,18 @@ ServerWebExchangeFirewall noOpFirewall() {
99113

100114
}
101115

116+
@Configuration
117+
static class CustomServerExchangeRejectedHandlerConfig {
118+
119+
static HttpStatus EXPECTED_STATUS = HttpStatus.I_AM_A_TEAPOT;
120+
121+
@Bean
122+
ServerExchangeRejectedHandler rejectedHandler() {
123+
return new HttpStatusExchangeRejectedHandler(EXPECTED_STATUS);
124+
}
125+
126+
}
127+
102128
@Configuration
103129
static class SubclassConfig extends WebFluxSecurityConfiguration {
104130

docs/modules/ROOT/pages/reactive/exploits/firewall.adoc

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,3 +200,35 @@ firewall.setAllowedHeaderValues {
200200
}
201201
----
202202
======
203+
204+
The `ServerExchangeRejectedHandler` interface is used to handle `ServerExchangeRejectedException` throw by Spring Security's `ServerWebExchangeFirewall`.
205+
By default `HttpStatusExchangeRejectedHandler` is used to send an HTTP 400 response to clients when a request is rejected.
206+
To customize the behavior, users can expose a `ServerExchangeRejectedHandler` Bean.
207+
For example, the following will send an HTTP 404 when the request is rejected:
208+
209+
210+
.Send 404 on Request Rejected
211+
[tabs]
212+
======
213+
Java::
214+
+
215+
[source,java,role="primary"]
216+
----
217+
@Bean
218+
ServerExchangeRejectedHandler rejectedHandler() {
219+
return new HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND);
220+
}
221+
----
222+
223+
Kotlin::
224+
+
225+
[source,kotlin,role="secondary"]
226+
----
227+
@Bean
228+
fun rejectedHandler(): ServerExchangeRejectedHandler {
229+
return HttpStatusExchangeRejectedHandler(HttpStatus.NOT_FOUND)
230+
}
231+
----
232+
======
233+
234+
Handling can be completely customized by creating a custom `ServerExchangeRejectedHandler` implementation.

0 commit comments

Comments
 (0)