Skip to content

Commit 9dc7bdd

Browse files
committed
Merge branch '6.1.x'
2 parents 8f5793a + cf2c8da commit 9dc7bdd

File tree

7 files changed

+237
-24
lines changed

7 files changed

+237
-24
lines changed

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

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@
1919
import java.util.ArrayList;
2020
import java.util.Arrays;
2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import jakarta.servlet.DispatcherType;
25+
import jakarta.servlet.ServletContext;
26+
import jakarta.servlet.ServletRegistration;
2427

2528
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2629
import org.springframework.context.ApplicationContext;
@@ -36,6 +39,7 @@
3639
import org.springframework.security.web.util.matcher.RequestMatcher;
3740
import org.springframework.util.Assert;
3841
import org.springframework.util.ClassUtils;
42+
import org.springframework.web.context.WebApplicationContext;
3943
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
4044

4145
/**
@@ -179,14 +183,47 @@ public C requestMatchers(RequestMatcher... requestMatchers) {
179183
* @since 5.8
180184
*/
181185
public C requestMatchers(HttpMethod method, String... patterns) {
182-
List<RequestMatcher> matchers = new ArrayList<>();
183-
if (mvcPresent) {
184-
matchers.addAll(createMvcMatchers(method, patterns));
186+
if (!mvcPresent) {
187+
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
188+
}
189+
if (!(this.context instanceof WebApplicationContext)) {
190+
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
191+
}
192+
WebApplicationContext context = (WebApplicationContext) this.context;
193+
ServletContext servletContext = context.getServletContext();
194+
if (servletContext == null) {
195+
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
196+
}
197+
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
198+
if (registrations == null) {
199+
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
200+
}
201+
if (!hasDispatcherServlet(registrations)) {
202+
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
185203
}
186-
else {
187-
matchers.addAll(RequestMatchers.antMatchers(method, patterns));
204+
Assert.isTrue(registrations.size() == 1,
205+
"This method cannot decide whether these patterns are Spring MVC patterns or not. If this endpoint is a Spring MVC endpoint, please use requestMatchers(MvcRequestMatcher); otherwise, please use requestMatchers(AntPathRequestMatcher).");
206+
return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0]));
207+
}
208+
209+
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
210+
if (registrations == null) {
211+
return false;
212+
}
213+
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
214+
null);
215+
for (ServletRegistration registration : registrations.values()) {
216+
try {
217+
Class<?> clazz = Class.forName(registration.getClassName());
218+
if (dispatcherServlet.isAssignableFrom(clazz)) {
219+
return true;
220+
}
221+
}
222+
catch (ClassNotFoundException ex) {
223+
return false;
224+
}
188225
}
189-
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
226+
return false;
190227
}
191228

192229
/**
@@ -262,12 +299,7 @@ private RequestMatchers() {
262299
* @return a {@link List} of {@link AntPathRequestMatcher} instances
263300
*/
264301
static List<RequestMatcher> antMatchers(HttpMethod httpMethod, String... antPatterns) {
265-
String method = (httpMethod != null) ? httpMethod.toString() : null;
266-
List<RequestMatcher> matchers = new ArrayList<>();
267-
for (String pattern : antPatterns) {
268-
matchers.add(new AntPathRequestMatcher(pattern, method));
269-
}
270-
return matchers;
302+
return Arrays.asList(antMatchersAsArray(httpMethod, antPatterns));
271303
}
272304

273305
/**
@@ -281,6 +313,15 @@ static List<RequestMatcher> antMatchers(String... antPatterns) {
281313
return antMatchers(null, antPatterns);
282314
}
283315

316+
static RequestMatcher[] antMatchersAsArray(HttpMethod httpMethod, String... antPatterns) {
317+
String method = (httpMethod != null) ? httpMethod.toString() : null;
318+
RequestMatcher[] matchers = new RequestMatcher[antPatterns.length];
319+
for (int index = 0; index < antPatterns.length; index++) {
320+
matchers[index] = new AntPathRequestMatcher(antPatterns[index], method);
321+
}
322+
return matchers;
323+
}
324+
284325
/**
285326
* Create a {@link List} of {@link RegexRequestMatcher} instances.
286327
* @param httpMethod the {@link HttpMethod} to use or {@code null} for any
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright 2002-2022 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.config;
18+
19+
import java.util.Collection;
20+
import java.util.LinkedHashMap;
21+
import java.util.Map;
22+
import java.util.Set;
23+
24+
import jakarta.servlet.MultipartConfigElement;
25+
import jakarta.servlet.Servlet;
26+
import jakarta.servlet.ServletRegistration;
27+
import jakarta.servlet.ServletSecurityElement;
28+
29+
import org.springframework.lang.NonNull;
30+
import org.springframework.web.servlet.DispatcherServlet;
31+
32+
public class MockServletContext extends org.springframework.mock.web.MockServletContext {
33+
34+
private final Map<String, ServletRegistration> registrations = new LinkedHashMap<>();
35+
36+
public static MockServletContext mvc() {
37+
MockServletContext servletContext = new MockServletContext();
38+
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
39+
return servletContext;
40+
}
41+
42+
@NonNull
43+
@Override
44+
public ServletRegistration.Dynamic addServlet(@NonNull String servletName, Class<? extends Servlet> clazz) {
45+
ServletRegistration.Dynamic dynamic = new MockServletRegistration(servletName, clazz);
46+
this.registrations.put(servletName, dynamic);
47+
return dynamic;
48+
}
49+
50+
@NonNull
51+
@Override
52+
public Map<String, ? extends ServletRegistration> getServletRegistrations() {
53+
return this.registrations;
54+
}
55+
56+
private static class MockServletRegistration implements ServletRegistration.Dynamic {
57+
58+
private final String name;
59+
60+
private final Class<?> clazz;
61+
62+
MockServletRegistration(String name, Class<?> clazz) {
63+
this.name = name;
64+
this.clazz = clazz;
65+
}
66+
67+
@Override
68+
public void setLoadOnStartup(int loadOnStartup) {
69+
70+
}
71+
72+
@Override
73+
public Set<String> setServletSecurity(ServletSecurityElement constraint) {
74+
return null;
75+
}
76+
77+
@Override
78+
public void setMultipartConfig(MultipartConfigElement multipartConfig) {
79+
80+
}
81+
82+
@Override
83+
public void setRunAsRole(String roleName) {
84+
85+
}
86+
87+
@Override
88+
public void setAsyncSupported(boolean isAsyncSupported) {
89+
90+
}
91+
92+
@Override
93+
public Set<String> addMapping(String... urlPatterns) {
94+
return null;
95+
}
96+
97+
@Override
98+
public Collection<String> getMappings() {
99+
return null;
100+
}
101+
102+
@Override
103+
public String getRunAsRole() {
104+
return null;
105+
}
106+
107+
@Override
108+
public String getName() {
109+
return this.name;
110+
}
111+
112+
@Override
113+
public String getClassName() {
114+
return this.clazz.getName();
115+
}
116+
117+
@Override
118+
public boolean setInitParameter(String name, String value) {
119+
return false;
120+
}
121+
122+
@Override
123+
public String getInitParameter(String name) {
124+
return null;
125+
}
126+
127+
@Override
128+
public Set<String> setInitParameters(Map<String, String> initParameters) {
129+
return null;
130+
}
131+
132+
@Override
133+
public Map<String, String> getInitParameters() {
134+
return null;
135+
}
136+
137+
}
138+
139+
}

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

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,22 @@
1919
import java.util.List;
2020

2121
import jakarta.servlet.DispatcherType;
22+
import jakarta.servlet.Servlet;
2223
import org.junit.jupiter.api.BeforeEach;
2324
import org.junit.jupiter.api.Test;
2425

2526
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2627
import org.springframework.context.ApplicationContext;
2728
import org.springframework.http.HttpMethod;
29+
import org.springframework.security.config.MockServletContext;
2830
import org.springframework.security.config.annotation.ObjectPostProcessor;
2931
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
3032
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
3133
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
3234
import org.springframework.security.web.util.matcher.RegexRequestMatcher;
3335
import org.springframework.security.web.util.matcher.RequestMatcher;
36+
import org.springframework.web.context.WebApplicationContext;
37+
import org.springframework.web.servlet.DispatcherServlet;
3438

3539
import static org.assertj.core.api.Assertions.assertThat;
3640
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -54,12 +58,15 @@ public <O> O postProcess(O object) {
5458

5559
private TestRequestMatcherRegistry matcherRegistry;
5660

61+
private WebApplicationContext context;
62+
5763
@BeforeEach
5864
public void setUp() {
5965
this.matcherRegistry = new TestRequestMatcherRegistry();
60-
ApplicationContext context = mock(ApplicationContext.class);
61-
given(context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
62-
this.matcherRegistry.setApplicationContext(context);
66+
this.context = mock(WebApplicationContext.class);
67+
given(this.context.getBean(ObjectPostProcessor.class)).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
68+
given(this.context.getServletContext()).willReturn(MockServletContext.mvc());
69+
this.matcherRegistry.setApplicationContext(this.context);
6370
mockMvcIntrospector(true);
6471
}
6572

@@ -147,6 +154,32 @@ public void requestMatchersWhenMvcPresentInClassPathAndMvcIntrospectorBeanNotAva
147154
"Please ensure Spring Security & Spring MVC are configured in a shared ApplicationContext");
148155
}
149156

157+
@Test
158+
public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() {
159+
MockServletContext servletContext = new MockServletContext();
160+
given(this.context.getServletContext()).willReturn(servletContext);
161+
List<RequestMatcher> requestMatchers = this.matcherRegistry.requestMatchers("/**");
162+
assertThat(requestMatchers).isNotEmpty();
163+
assertThat(requestMatchers).hasSize(1);
164+
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
165+
servletContext.addServlet("servletOne", Servlet.class);
166+
servletContext.addServlet("servletTwo", Servlet.class);
167+
requestMatchers = this.matcherRegistry.requestMatchers("/**");
168+
assertThat(requestMatchers).isNotEmpty();
169+
assertThat(requestMatchers).hasSize(1);
170+
assertThat(requestMatchers.get(0)).isExactlyInstanceOf(AntPathRequestMatcher.class);
171+
}
172+
173+
@Test
174+
public void requestMatchersWhenAmbiguousServletsThenException() {
175+
MockServletContext servletContext = new MockServletContext();
176+
given(this.context.getServletContext()).willReturn(servletContext);
177+
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class);
178+
servletContext.addServlet("servletTwo", Servlet.class);
179+
assertThatExceptionOfType(IllegalArgumentException.class)
180+
.isThrownBy(() -> this.matcherRegistry.requestMatchers("/**"));
181+
}
182+
150183
private void mockMvcIntrospector(boolean isPresent) {
151184
ApplicationContext context = this.matcherRegistry.getApplicationContext();
152185
given(context.containsBean("mvcHandlerMappingIntrospector")).willReturn(isPresent);

config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeRequestsTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@
2828
import org.springframework.mock.web.MockFilterChain;
2929
import org.springframework.mock.web.MockHttpServletRequest;
3030
import org.springframework.mock.web.MockHttpServletResponse;
31-
import org.springframework.mock.web.MockServletContext;
3231
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
3332
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
3433
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
34+
import org.springframework.security.config.MockServletContext;
3535
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3636
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3737
import org.springframework.security.core.authority.AuthorityUtils;
@@ -77,7 +77,7 @@ public class AuthorizeRequestsTests {
7777

7878
@BeforeEach
7979
public void setup() {
80-
this.servletContext = spy(new MockServletContext());
80+
this.servletContext = spy(MockServletContext.mvc());
8181
this.request = new MockHttpServletRequest("GET", "");
8282
this.request.setMethod("GET");
8383
this.response = new MockHttpServletResponse();

config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import org.springframework.mock.web.MockFilterChain;
3333
import org.springframework.mock.web.MockHttpServletRequest;
3434
import org.springframework.mock.web.MockHttpServletResponse;
35-
import org.springframework.mock.web.MockServletContext;
35+
import org.springframework.security.config.MockServletContext;
3636
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3737
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3838
import org.springframework.security.core.userdetails.User;
@@ -220,7 +220,7 @@ public void securityMatchersWhenMultiMvcMatcherThenAllPathsAreDenied() throws Ex
220220
public void loadConfig(Class<?>... configs) {
221221
this.context = new AnnotationConfigWebApplicationContext();
222222
this.context.register(configs);
223-
this.context.setServletContext(new MockServletContext());
223+
this.context.setServletContext(MockServletContext.mvc());
224224
this.context.refresh();
225225
this.context.getAutowireCapableBeanFactory().autowireBean(this);
226226
}

config/src/test/java/org/springframework/security/config/annotation/web/configurers/UrlAuthorizationConfigurerTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
import org.springframework.mock.web.MockFilterChain;
3131
import org.springframework.mock.web.MockHttpServletRequest;
3232
import org.springframework.mock.web.MockHttpServletResponse;
33-
import org.springframework.mock.web.MockServletContext;
3433
import org.springframework.security.config.Customizer;
34+
import org.springframework.security.config.MockServletContext;
3535
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3636
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
3737
import org.springframework.security.core.userdetails.PasswordEncodedUser;
@@ -167,7 +167,7 @@ public void multiMvcMatchersConfig() throws Exception {
167167
public void loadConfig(Class<?>... configs) {
168168
this.context = new AnnotationConfigWebApplicationContext();
169169
this.context.register(configs);
170-
this.context.setServletContext(new MockServletContext());
170+
this.context.setServletContext(MockServletContext.mvc());
171171
this.context.refresh();
172172
this.context.getAutowireCapableBeanFactory().autowireBean(this);
173173
}

0 commit comments

Comments
 (0)