Skip to content

Commit c24b5eb

Browse files
committedNov 11, 2024
Support ServerExchangeRejectedHandler @bean
Closes gh-15975
1 parent fd900c2 commit c24b5eb

File tree

3 files changed

+64
-1
lines changed

3 files changed

+64
-1
lines changed
 

Diff for: ‎config/src/main/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfiguration.java

+4-1
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

Diff for: ‎config/src/test/java/org/springframework/security/config/annotation/web/reactive/WebFluxSecurityConfigurationTests.java

+28
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

@@ -70,6 +72,20 @@ void loadConfigWhenDefaultThenFirewalled() throws Exception {
7072
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
7173
}
7274

75+
@Test
76+
void loadConfigWhenCustomRejectedHandler() throws Exception {
77+
this.spring
78+
.register(ServerHttpSecurityConfiguration.class, ReactiveAuthenticationTestConfiguration.class,
79+
WebFluxSecurityConfiguration.class, CustomServerExchangeRejectedHandlerConfig.class)
80+
.autowire();
81+
WebFilterChainProxy webFilterChainProxy = this.spring.getContext().getBean(WebFilterChainProxy.class);
82+
MockServerWebExchange exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/;/").build());
83+
DefaultWebFilterChain chain = emptyChain();
84+
webFilterChainProxy.filter(exchange, chain).block();
85+
assertThat(exchange.getResponse().getStatusCode())
86+
.isEqualTo(CustomServerExchangeRejectedHandlerConfig.EXPECTED_STATUS);
87+
}
88+
7389
@Test
7490
void loadConfigWhenFirewallBeanThenCustomized() throws Exception {
7591
this.spring
@@ -107,6 +123,18 @@ ServerWebExchangeFirewall noOpFirewall() {
107123

108124
}
109125

126+
@Configuration
127+
static class CustomServerExchangeRejectedHandlerConfig {
128+
129+
static HttpStatus EXPECTED_STATUS = HttpStatus.I_AM_A_TEAPOT;
130+
131+
@Bean
132+
ServerExchangeRejectedHandler rejectedHandler() {
133+
return new HttpStatusExchangeRejectedHandler(EXPECTED_STATUS);
134+
}
135+
136+
}
137+
110138
@Configuration
111139
static class SubclassConfig extends WebFluxSecurityConfiguration {
112140

Diff for: ‎docs/modules/ROOT/pages/reactive/exploits/firewall.adoc

+32
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)