Skip to content

Commit de5bfa1

Browse files
author
Adam Ostrožlík
committed
Support multiple RequestRejectedHandler beans by using CompositeRequestRejectedHandler
1 parent 1d98961 commit de5bfa1

File tree

5 files changed

+66
-68
lines changed

5 files changed

+66
-68
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/builders/WebSecurity.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,6 @@ public final class WebSecurity extends AbstractConfiguredSecurityBuilder<Filter,
9595

9696
private HttpFirewall httpFirewall;
9797

98-
private Collection<RequestRejectedHandler> requestRejectedHandlers;
99-
10098
private boolean debugEnabled;
10199

102100
private WebInvocationPrivilegeEvaluator privilegeEvaluator;
@@ -268,17 +266,6 @@ public WebSecurity postBuildAction(Runnable postBuildAction) {
268266
return this;
269267
}
270268

271-
/**
272-
* Sets the handlers to handle {@link org.springframework.security.web.firewall.RequestRejectedException}
273-
* @param requestRejectedHandlers
274-
* @return the {@link WebSecurity} for further customizations
275-
*/
276-
public WebSecurity requestRejectedHandlers(RequestRejectedHandler... requestRejectedHandlers) {
277-
Assert.notNull(requestRejectedHandlers, "requestRejectedHandlers cannot be null");
278-
this.requestRejectedHandlers = Arrays.asList(requestRejectedHandlers);
279-
return this;
280-
}
281-
282269
@Override
283270
protected Filter performBuild() throws Exception {
284271
Assert.state(!this.securityFilterChainBuilders.isEmpty(),

web/src/main/java/org/springframework/security/web/FilterChainProxy.java

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
1818

1919
import java.io.IOException;
2020
import java.util.Arrays;
21-
import java.util.Collection;
2221
import java.util.Collections;
2322
import java.util.List;
2423

@@ -153,7 +152,7 @@ public class FilterChainProxy extends GenericFilterBean {
153152

154153
private HttpFirewall firewall = new StrictHttpFirewall();
155154

156-
private Collection<RequestRejectedHandler> requestRejectedHandlers = List.of(new DefaultRequestRejectedHandler());
155+
private RequestRejectedHandler requestRejectedHandler = new DefaultRequestRejectedHandler();
157156

158157
public FilterChainProxy() {
159158
}
@@ -184,25 +183,14 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
184183
doFilterInternal(request, response, chain);
185184
}
186185
catch (RequestRejectedException ex) {
187-
handleRequestRejectedException((HttpServletRequest) request, (HttpServletResponse) response, ex);
186+
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
188187
}
189188
finally {
190189
SecurityContextHolder.clearContext();
191190
request.removeAttribute(FILTER_APPLIED);
192191
}
193192
}
194193

195-
private void handleRequestRejectedException(HttpServletRequest request, HttpServletResponse response, RequestRejectedException ex) throws ServletException, IOException {
196-
var handlerOpt = this.requestRejectedHandlers.stream()
197-
.filter(handler -> handler.shouldHandle(request))
198-
.findFirst();
199-
if (handlerOpt.isPresent()) {
200-
handlerOpt.get().handle(request, response, ex);
201-
} else {
202-
new DefaultRequestRejectedHandler().handle(request, response, ex);
203-
}
204-
}
205-
206194
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
207195
throws IOException, ServletException {
208196
FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
@@ -282,15 +270,12 @@ public void setFirewall(HttpFirewall firewall) {
282270
/**
283271
* Sets the {@link RequestRejectedHandler} to be used for requests rejected by the
284272
* firewall.
285-
* @param requestRejectedHandlers the {@link RequestRejectedHandler}
273+
* @param requestRejectedHandler the {@link RequestRejectedHandler}
286274
* @since 5.2
287275
*/
288-
public void setRequestRejectedHandlers(Collection<RequestRejectedHandler> requestRejectedHandlers) {
289-
Assert.notNull(requestRejectedHandlers, "requestRejectedHandlers may not be null");
290-
if (requestRejectedHandlers.isEmpty()) {
291-
return;
292-
}
293-
this.requestRejectedHandlers = requestRejectedHandlers;
276+
public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
277+
Assert.notNull(requestRejectedHandler, "requestRejectedHandler may not be null");
278+
this.requestRejectedHandler = requestRejectedHandler;
294279
}
295280

296281
@Override
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.web.firewall;
18+
19+
import jakarta.servlet.ServletException;
20+
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.HttpServletResponse;
22+
import org.springframework.security.web.header.HeaderWriter;
23+
import org.springframework.util.Assert;
24+
25+
import java.io.IOException;
26+
import java.util.List;
27+
28+
/**
29+
* A {@link RequestRejectedHandler} that delegates to several other {@link RequestRejectedHandler}s.
30+
*
31+
* @author Adam Ostrožlík
32+
* @since 5.7
33+
*/
34+
public class CompositeRequestRejectedHandler implements RequestRejectedHandler {
35+
36+
private final List<RequestRejectedHandler> requestRejectedhandlers;
37+
38+
/**
39+
* Creates a new instance.
40+
* @param requestRejectedhandlers the {@link RequestRejectedHandler} instances to handle {@link org.springframework.security.web.firewall.RequestRejectedException}
41+
*/
42+
public CompositeRequestRejectedHandler(List<RequestRejectedHandler> requestRejectedhandlers) {
43+
Assert.notEmpty(requestRejectedhandlers, "requestRejectedhandlers cannot be empty");
44+
this.requestRejectedhandlers = requestRejectedhandlers;
45+
}
46+
47+
@Override
48+
public void handle(HttpServletRequest request, HttpServletResponse response, RequestRejectedException requestRejectedException) throws IOException, ServletException {
49+
for (RequestRejectedHandler requestRejectedhandler : requestRejectedhandlers) {
50+
requestRejectedhandler.handle(request, response, requestRejectedException);
51+
}
52+
}
53+
}

web/src/main/java/org/springframework/security/web/firewall/RequestRejectedHandler.java

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,8 @@
2525
/**
2626
* Used by {@link org.springframework.security.web.FilterChainProxy} to handle an
2727
* <code>RequestRejectedException</code>.
28-
* Supports multiple beans of this handler. The handler is chosen by the result of
29-
* <code>shouldHandle</code> method - <code>true</code> means it is chosen.
30-
* If there are more than one handler to choose from, the first matching is used.
31-
* If no suitable handler is found, the {@link DefaultRequestRejectedHandler} is used.
3228
*
3329
* @author Leonard Brünings
34-
* @author Adam Ostrožlík
3530
* @since 5.4
3631
*/
3732
public interface RequestRejectedHandler {
@@ -47,12 +42,4 @@ public interface RequestRejectedHandler {
4742
void handle(HttpServletRequest request, HttpServletResponse response,
4843
RequestRejectedException requestRejectedException) throws IOException, ServletException;
4944

50-
/**
51-
* Determines if this handler should be invoked.
52-
* First available handler is invoked if there are multiple suitables ones.
53-
* @param request that resulted in an <code>RequestRejectedException</code>
54-
*/
55-
default boolean shouldHandle(HttpServletRequest request) {
56-
return true;
57-
}
5845
}

web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,7 +49,9 @@
4949
import static org.mockito.ArgumentMatchers.eq;
5050
import static org.mockito.BDDMockito.given;
5151
import static org.mockito.BDDMockito.willAnswer;
52-
import static org.mockito.Mockito.*;
52+
import static org.mockito.Mockito.mock;
53+
import static org.mockito.Mockito.verify;
54+
import static org.mockito.Mockito.verifyZeroInteractions;
5355

5456
/**
5557
* @author Luke Taylor
@@ -235,35 +237,19 @@ public void doFilterClearsSecurityContextHolderOnceOnForwards() throws Exception
235237

236238
@Test
237239
public void setRequestRejectedHandlerDoesNotAcceptNull() {
238-
assertThatIllegalArgumentException().isThrownBy(() -> this.fcp.setRequestRejectedHandlers(null));
240+
assertThatIllegalArgumentException().isThrownBy(() -> this.fcp.setRequestRejectedHandler(null));
239241
}
240242

241243
@Test
242244
public void requestRejectedHandlerIsCalledIfFirewallThrowsRequestRejectedException() throws Exception {
243245
HttpFirewall fw = mock(HttpFirewall.class);
244246
RequestRejectedHandler rjh = mock(RequestRejectedHandler.class);
245-
when(rjh.shouldHandle(this.request)).thenReturn(true);
246247
this.fcp.setFirewall(fw);
247-
this.fcp.setRequestRejectedHandlers(List.of(rjh));
248+
this.fcp.setRequestRejectedHandler(rjh);
248249
RequestRejectedException requestRejectedException = new RequestRejectedException("Contains illegal chars");
249250
given(fw.getFirewalledRequest(this.request)).willThrow(requestRejectedException);
250251
this.fcp.doFilter(this.request, this.response, this.chain);
251252
verify(rjh).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));
252253
}
253254

254-
@Test
255-
public void oneOfRequestRejectedHandlerIsCalledIfFirewallThrowsRequestRejectedException() throws Exception {
256-
HttpFirewall fw = mock(HttpFirewall.class);
257-
RequestRejectedHandler rjh1 = mock(RequestRejectedHandler.class);
258-
RequestRejectedHandler rjh2 = mock(RequestRejectedHandler.class);
259-
when(rjh1.shouldHandle(this.request)).thenReturn(false);
260-
when(rjh2.shouldHandle(this.request)).thenReturn(true);
261-
this.fcp.setFirewall(fw);
262-
this.fcp.setRequestRejectedHandlers(List.of(rjh1, rjh2));
263-
RequestRejectedException requestRejectedException = new RequestRejectedException("Contains illegal chars");
264-
given(fw.getFirewalledRequest(this.request)).willThrow(requestRejectedException);
265-
this.fcp.doFilter(this.request, this.response, this.chain);
266-
verify(rjh2).handle(eq(this.request), eq(this.response), eq((requestRejectedException)));
267-
}
268-
269255
}

0 commit comments

Comments
 (0)