Skip to content

Commit 5958828

Browse files
committed
Merge branch '6.1.x'
2 parents 4491b95 + aa1a022 commit 5958828

File tree

2 files changed

+95
-15
lines changed

2 files changed

+95
-15
lines changed

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

+72-15
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.LinkedHashMap;
2323
import java.util.List;
2424
import java.util.Map;
25+
import java.util.concurrent.atomic.AtomicReference;
26+
import java.util.function.Supplier;
2527

2628
import jakarta.servlet.DispatcherType;
2729
import jakarta.servlet.ServletContext;
@@ -42,6 +44,7 @@
4244
import org.springframework.security.web.util.matcher.RequestMatcher;
4345
import org.springframework.util.Assert;
4446
import org.springframework.util.ClassUtils;
47+
import org.springframework.util.function.SingletonSupplier;
4548
import org.springframework.web.context.WebApplicationContext;
4649
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
4750

@@ -197,34 +200,51 @@ public C requestMatchers(HttpMethod method, String... patterns) {
197200
if (servletContext == null) {
198201
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
199202
}
203+
boolean isProgrammaticApiAvailable = isProgrammaticApiAvailable(servletContext);
204+
List<RequestMatcher> matchers = new ArrayList<>();
205+
for (String pattern : patterns) {
206+
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
207+
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
208+
if (isProgrammaticApiAvailable) {
209+
matchers.add(resolve(ant, mvc, servletContext));
210+
}
211+
else {
212+
matchers.add(new DeferredRequestMatcher(() -> resolve(ant, mvc, servletContext), mvc, ant));
213+
}
214+
}
215+
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
216+
}
217+
218+
private static boolean isProgrammaticApiAvailable(ServletContext servletContext) {
219+
try {
220+
servletContext.getServletRegistrations();
221+
return true;
222+
}
223+
catch (UnsupportedOperationException ex) {
224+
return false;
225+
}
226+
}
227+
228+
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
200229
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
201230
if (registrations.isEmpty()) {
202-
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
231+
return ant;
203232
}
204233
if (!hasDispatcherServlet(registrations)) {
205-
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
234+
return ant;
206235
}
207236
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
208237
if (dispatcherServlet != null) {
209238
if (registrations.size() == 1) {
210-
return requestMatchers(createMvcMatchers(method, patterns).toArray(RequestMatcher[]::new));
239+
return mvc;
211240
}
212-
List<RequestMatcher> matchers = new ArrayList<>();
213-
for (String pattern : patterns) {
214-
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
215-
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
216-
matchers.add(new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext));
217-
}
218-
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
241+
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
219242
}
220243
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
221244
if (dispatcherServlet != null) {
222245
String mapping = dispatcherServlet.getMappings().iterator().next();
223-
List<MvcRequestMatcher> matchers = createMvcMatchers(method, patterns);
224-
for (MvcRequestMatcher matcher : matchers) {
225-
matcher.setServletPath(mapping.substring(0, mapping.length() - 2));
226-
}
227-
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
246+
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
247+
return mvc;
228248
}
229249
String errorMessage = computeErrorMessage(registrations.values());
230250
throw new IllegalArgumentException(errorMessage);
@@ -444,6 +464,38 @@ static List<RequestMatcher> regexMatchers(String... regexPatterns) {
444464

445465
}
446466

467+
static class DeferredRequestMatcher implements RequestMatcher {
468+
469+
final Supplier<RequestMatcher> requestMatcher;
470+
471+
final AtomicReference<String> description = new AtomicReference<>();
472+
473+
DeferredRequestMatcher(Supplier<RequestMatcher> resolver, RequestMatcher... candidates) {
474+
this.requestMatcher = SingletonSupplier.of(() -> {
475+
RequestMatcher matcher = resolver.get();
476+
this.description.set(matcher.toString());
477+
return matcher;
478+
});
479+
this.description.set("Deferred " + candidates);
480+
}
481+
482+
@Override
483+
public boolean matches(HttpServletRequest request) {
484+
return this.requestMatcher.get().matches(request);
485+
}
486+
487+
@Override
488+
public MatchResult matcher(HttpServletRequest request) {
489+
return this.requestMatcher.get().matcher(request);
490+
}
491+
492+
@Override
493+
public String toString() {
494+
return this.description.get();
495+
}
496+
497+
}
498+
447499
static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
448500

449501
private final AntPathRequestMatcher ant;
@@ -493,6 +545,11 @@ private boolean isDispatcherServlet(ServletRegistration registration) {
493545
}
494546
}
495547

548+
@Override
549+
public String toString() {
550+
return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]";
551+
}
552+
496553
}
497554

498555
}

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

+23
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.security.config.annotation.web;
1818

19+
import java.util.ArrayList;
1920
import java.util.List;
2021

2122
import jakarta.servlet.DispatcherType;
@@ -163,6 +164,7 @@ public void requestMatchersWhenMvcPresentInClassPathAndMvcIntrospectorBeanNotAva
163164

164165
@Test
165166
public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() {
167+
mockMvcIntrospector(true);
166168
MockServletContext servletContext = new MockServletContext();
167169
given(this.context.getServletContext()).willReturn(servletContext);
168170
servletContext.addServlet("servletOne", Servlet.class).addMapping("/one");
@@ -175,6 +177,7 @@ public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType(
175177

176178
@Test
177179
public void requestMatchersWhenAmbiguousServletsThenException() {
180+
mockMvcIntrospector(true);
178181
MockServletContext servletContext = new MockServletContext();
179182
given(this.context.getServletContext()).willReturn(servletContext);
180183
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
@@ -185,6 +188,7 @@ public void requestMatchersWhenAmbiguousServletsThenException() {
185188

186189
@Test
187190
public void requestMatchersWhenMultipleDispatcherServletMappingsThenException() {
191+
mockMvcIntrospector(true);
188192
MockServletContext servletContext = new MockServletContext();
189193
given(this.context.getServletContext()).willReturn(servletContext);
190194
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/mvc/*");
@@ -194,6 +198,7 @@ public void requestMatchersWhenMultipleDispatcherServletMappingsThenException()
194198

195199
@Test
196200
public void requestMatchersWhenPathDispatcherServletAndOtherServletsThenException() {
201+
mockMvcIntrospector(true);
197202
MockServletContext servletContext = new MockServletContext();
198203
given(this.context.getServletContext()).willReturn(servletContext);
199204
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*");
@@ -282,11 +287,29 @@ private void mockMvcIntrospector(boolean isPresent) {
282287

283288
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
284289

290+
@Override
291+
public List<RequestMatcher> requestMatchers(RequestMatcher... requestMatchers) {
292+
return unwrap(super.requestMatchers(requestMatchers));
293+
}
294+
285295
@Override
286296
protected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> requestMatchers) {
287297
return requestMatchers;
288298
}
289299

300+
private static List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) {
301+
List<RequestMatcher> requestMatchers = new ArrayList<>();
302+
for (RequestMatcher requestMatcher : wrappedMatchers) {
303+
if (requestMatcher instanceof AbstractRequestMatcherRegistry.DeferredRequestMatcher) {
304+
requestMatchers.add(((DeferredRequestMatcher) requestMatcher).requestMatcher.get());
305+
}
306+
else {
307+
requestMatchers.add(requestMatcher);
308+
}
309+
}
310+
return requestMatchers;
311+
}
312+
290313
}
291314

292315
}

0 commit comments

Comments
 (0)