|
22 | 22 | import java.util.LinkedHashMap;
|
23 | 23 | import java.util.List;
|
24 | 24 | import java.util.Map;
|
| 25 | +import java.util.concurrent.atomic.AtomicReference; |
| 26 | +import java.util.function.Supplier; |
25 | 27 |
|
26 | 28 | import jakarta.servlet.DispatcherType;
|
27 | 29 | import jakarta.servlet.ServletContext;
|
|
42 | 44 | import org.springframework.security.web.util.matcher.RequestMatcher;
|
43 | 45 | import org.springframework.util.Assert;
|
44 | 46 | import org.springframework.util.ClassUtils;
|
| 47 | +import org.springframework.util.function.SingletonSupplier; |
45 | 48 | import org.springframework.web.context.WebApplicationContext;
|
46 | 49 | import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
47 | 50 |
|
@@ -197,34 +200,51 @@ public C requestMatchers(HttpMethod method, String... patterns) {
|
197 | 200 | if (servletContext == null) {
|
198 | 201 | return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
199 | 202 | }
|
| 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) { |
200 | 229 | Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
201 | 230 | if (registrations.isEmpty()) {
|
202 |
| - return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); |
| 231 | + return ant; |
203 | 232 | }
|
204 | 233 | if (!hasDispatcherServlet(registrations)) {
|
205 |
| - return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); |
| 234 | + return ant; |
206 | 235 | }
|
207 | 236 | ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
208 | 237 | if (dispatcherServlet != null) {
|
209 | 238 | if (registrations.size() == 1) {
|
210 |
| - return requestMatchers(createMvcMatchers(method, patterns).toArray(RequestMatcher[]::new)); |
| 239 | + return mvc; |
211 | 240 | }
|
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); |
219 | 242 | }
|
220 | 243 | dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
221 | 244 | if (dispatcherServlet != null) {
|
222 | 245 | 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; |
228 | 248 | }
|
229 | 249 | String errorMessage = computeErrorMessage(registrations.values());
|
230 | 250 | throw new IllegalArgumentException(errorMessage);
|
@@ -444,6 +464,38 @@ static List<RequestMatcher> regexMatchers(String... regexPatterns) {
|
444 | 464 |
|
445 | 465 | }
|
446 | 466 |
|
| 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 | + |
447 | 499 | static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
|
448 | 500 |
|
449 | 501 | private final AntPathRequestMatcher ant;
|
@@ -493,6 +545,11 @@ private boolean isDispatcherServlet(ServletRegistration registration) {
|
493 | 545 | }
|
494 | 546 | }
|
495 | 547 |
|
| 548 | + @Override |
| 549 | + public String toString() { |
| 550 | + return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]"; |
| 551 | + } |
| 552 | + |
496 | 553 | }
|
497 | 554 |
|
498 | 555 | }
|
0 commit comments