Skip to content

Commit 0838d4d

Browse files
committed
Introduced DispatcherType request matcher
Created a DispatcherTypeRequestMatcher and corresponding methods for configuring an HttpSecurity object. This enables filtering of security rules based on the dispatcher type of the incoming servlet request. Closes gh-9205
1 parent 48ef27b commit 0838d4d

File tree

4 files changed

+216
-0
lines changed

4 files changed

+216
-0
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@
2020
import java.util.Arrays;
2121
import java.util.List;
2222

23+
import javax.servlet.DispatcherType;
24+
2325
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2426
import org.springframework.context.ApplicationContext;
2527
import org.springframework.http.HttpMethod;
28+
import org.springframework.lang.Nullable;
2629
import org.springframework.security.config.annotation.ObjectPostProcessor;
2730
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
2831
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
2932
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
3033
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
34+
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
3135
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
3236
import org.springframework.security.web.util.matcher.RequestMatcher;
3337
import org.springframework.util.Assert;
@@ -206,6 +210,36 @@ public C regexMatchers(String... regexPatterns) {
206210
return chainRequestMatchers(RequestMatchers.regexMatchers(regexPatterns));
207211
}
208212

213+
/**
214+
* Maps a {@link List} of
215+
* {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}
216+
* instances.
217+
* @param method the {@link HttpMethod} to use or {@code null} for any
218+
* {@link HttpMethod}.
219+
* @param dispatcherTypes the dispatcher types to match against
220+
* @return the object that is chained after creating the {@link RequestMatcher}
221+
*/
222+
public C dispatcherTypeMatchers(@Nullable HttpMethod method, DispatcherType... dispatcherTypes) {
223+
Assert.state(!this.anyRequestConfigured, "Can't configure dispatcherTypeMatchers after anyRequest");
224+
List<RequestMatcher> matchers = new ArrayList<>();
225+
for (DispatcherType dispatcherType : dispatcherTypes) {
226+
matchers.add(new DispatcherTypeRequestMatcher(dispatcherType, method));
227+
}
228+
return chainRequestMatchers(matchers);
229+
}
230+
231+
/**
232+
* Create a {@link List} of
233+
* {@link org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher}
234+
* instances that do not specify an {@link HttpMethod}.
235+
* @param dispatcherTypes the dispatcher types to match against
236+
* @return the object that is chained after creating the {@link RequestMatcher}
237+
*/
238+
public C dispatcherTypeMatchers(DispatcherType... dispatcherTypes) {
239+
Assert.state(!this.anyRequestConfigured, "Can't configure dispatcherTypeMatchers after anyRequest");
240+
return dispatcherTypeMatchers(null, dispatcherTypes);
241+
}
242+
209243
/**
210244
* Associates a list of {@link RequestMatcher} instances with the
211245
* {@link AbstractConfigAttributeRequestMatcherRegistry}

config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@
1818

1919
import java.util.List;
2020

21+
import javax.servlet.DispatcherType;
22+
2123
import org.junit.Before;
2224
import org.junit.Test;
2325

2426
import org.springframework.http.HttpMethod;
2527
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
28+
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
2629
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
2730
import org.springframework.security.web.util.matcher.RequestMatcher;
2831

@@ -74,6 +77,23 @@ public void antMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() {
7477
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
7578
}
7679

80+
@Test
81+
public void dispatcherTypeMatchersWhenHttpMethodAndPatternParamsThenReturnAntPathRequestMatcherType() {
82+
List<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(HttpMethod.GET,
83+
DispatcherType.ASYNC);
84+
assertThat(requestMatchers).isNotEmpty();
85+
assertThat(requestMatchers.size()).isEqualTo(1);
86+
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
87+
}
88+
89+
@Test
90+
public void dispatcherMatchersWhenPatternParamThenReturnAntPathRequestMatcherType() {
91+
List<RequestMatcher> requestMatchers = this.matcherRegistry.dispatcherTypeMatchers(DispatcherType.INCLUDE);
92+
assertThat(requestMatchers).isNotEmpty();
93+
assertThat(requestMatchers.size()).isEqualTo(1);
94+
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(DispatcherTypeRequestMatcher.class);
95+
}
96+
7797
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
7898

7999
@Override
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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.util.matcher;
18+
19+
import javax.servlet.DispatcherType;
20+
import javax.servlet.http.HttpServletRequest;
21+
22+
import org.springframework.http.HttpMethod;
23+
import org.springframework.lang.Nullable;
24+
import org.springframework.util.StringUtils;
25+
26+
/**
27+
* Checks the {@link DispatcherType} to decide whether to match a given request.
28+
* {@code HttpServletRequest}.
29+
*
30+
* Can also be configured to match a specific HTTP method.
31+
*
32+
* @author Nick McKinney
33+
* @since 5.5
34+
*/
35+
public class DispatcherTypeRequestMatcher implements RequestMatcher {
36+
37+
private final DispatcherType dispatcherType;
38+
39+
@Nullable
40+
private final HttpMethod httpMethod;
41+
42+
/**
43+
* Creates an instance which matches requests with the provided {@link DispatcherType}
44+
* @param dispatcherType the type to match against
45+
*/
46+
public DispatcherTypeRequestMatcher(DispatcherType dispatcherType) {
47+
this(dispatcherType, null);
48+
}
49+
50+
/**
51+
* Creates an instance which matches requests with the provided {@link DispatcherType}
52+
* and {@link HttpMethod}
53+
* @param dispatcherType the type to match against
54+
* @param httpMethod the HTTP method to match. May be null to match all methods.
55+
*/
56+
public DispatcherTypeRequestMatcher(DispatcherType dispatcherType, @Nullable HttpMethod httpMethod) {
57+
this.dispatcherType = dispatcherType;
58+
this.httpMethod = httpMethod;
59+
}
60+
61+
/**
62+
* Performs the match against the request's method and dispatcher type.
63+
* @param request the request to check for a match
64+
* @return true if the http method and dispatcher type align
65+
*/
66+
@Override
67+
public boolean matches(HttpServletRequest request) {
68+
if (this.httpMethod != null && StringUtils.hasText(request.getMethod())
69+
&& this.httpMethod != HttpMethod.resolve(request.getMethod())) {
70+
return false;
71+
}
72+
return this.dispatcherType == request.getDispatcherType();
73+
}
74+
75+
@Override
76+
public String toString() {
77+
return "DispatcherTypeRequestMatcher{" + "dispatcherType=" + this.dispatcherType + ", httpMethod="
78+
+ this.httpMethod + '}';
79+
}
80+
81+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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.util.matcher;
18+
19+
import javax.servlet.DispatcherType;
20+
import javax.servlet.http.HttpServletRequest;
21+
22+
import org.junit.Test;
23+
24+
import org.springframework.http.HttpMethod;
25+
import org.springframework.mock.web.MockHttpServletRequest;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
29+
/**
30+
* @author Nick McKinney
31+
*/
32+
public class DispatcherTypeRequestMatcherTests {
33+
34+
@Test
35+
public void matches_dispatcher_type() {
36+
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
37+
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);
38+
39+
assertThat(matcher.matches(request)).isTrue();
40+
}
41+
42+
@Test
43+
public void matches_dispatcher_type_and_http_method() {
44+
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
45+
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.GET);
46+
47+
assertThat(matcher.matches(request)).isTrue();
48+
}
49+
50+
@Test
51+
public void does_not_match_wrong_type() {
52+
HttpServletRequest request = mockHttpServletRequest(DispatcherType.FORWARD, HttpMethod.GET);
53+
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR);
54+
55+
assertThat(matcher.matches(request)).isFalse();
56+
}
57+
58+
@Test
59+
public void does_not_match_with_wrong_http_method() {
60+
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.GET);
61+
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, HttpMethod.POST);
62+
63+
assertThat(matcher.matches(request)).isFalse();
64+
}
65+
66+
@Test
67+
public void null_http_method_matches_any_http_method() {
68+
HttpServletRequest request = mockHttpServletRequest(DispatcherType.ERROR, HttpMethod.POST);
69+
DispatcherTypeRequestMatcher matcher = new DispatcherTypeRequestMatcher(DispatcherType.ERROR, null);
70+
71+
assertThat(matcher.matches(request)).isTrue();
72+
}
73+
74+
private HttpServletRequest mockHttpServletRequest(DispatcherType dispatcherType, HttpMethod httpMethod) {
75+
MockHttpServletRequest mockHttpServletRequest = new MockHttpServletRequest();
76+
mockHttpServletRequest.setDispatcherType(dispatcherType);
77+
mockHttpServletRequest.setMethod(httpMethod.name());
78+
return mockHttpServletRequest;
79+
}
80+
81+
}

0 commit comments

Comments
 (0)