|
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 javax.servlet.DispatcherType;
|
27 | 29 | import javax.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 |
|
@@ -315,34 +318,51 @@ public C requestMatchers(HttpMethod method, String... patterns) {
|
315 | 318 | if (servletContext == null) {
|
316 | 319 | return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
317 | 320 | }
|
| 321 | + boolean isProgrammaticApiAvailable = isProgrammaticApiAvailable(servletContext); |
| 322 | + List<RequestMatcher> matchers = new ArrayList<>(); |
| 323 | + for (String pattern : patterns) { |
| 324 | + AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null); |
| 325 | + MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0); |
| 326 | + if (isProgrammaticApiAvailable) { |
| 327 | + matchers.add(resolve(ant, mvc, servletContext)); |
| 328 | + } |
| 329 | + else { |
| 330 | + matchers.add(new DeferredRequestMatcher(() -> resolve(ant, mvc, servletContext), mvc, ant)); |
| 331 | + } |
| 332 | + } |
| 333 | + return requestMatchers(matchers.toArray(new RequestMatcher[0])); |
| 334 | + } |
| 335 | + |
| 336 | + private static boolean isProgrammaticApiAvailable(ServletContext servletContext) { |
| 337 | + try { |
| 338 | + servletContext.getServletRegistrations(); |
| 339 | + return true; |
| 340 | + } |
| 341 | + catch (UnsupportedOperationException ex) { |
| 342 | + return false; |
| 343 | + } |
| 344 | + } |
| 345 | + |
| 346 | + private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) { |
318 | 347 | Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
319 | 348 | if (registrations.isEmpty()) {
|
320 |
| - return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); |
| 349 | + return ant; |
321 | 350 | }
|
322 | 351 | if (!hasDispatcherServlet(registrations)) {
|
323 |
| - return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); |
| 352 | + return ant; |
324 | 353 | }
|
325 | 354 | ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
326 | 355 | if (dispatcherServlet != null) {
|
327 | 356 | if (registrations.size() == 1) {
|
328 |
| - return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0])); |
| 357 | + return mvc; |
329 | 358 | }
|
330 |
| - List<RequestMatcher> matchers = new ArrayList<>(); |
331 |
| - for (String pattern : patterns) { |
332 |
| - AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null); |
333 |
| - MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0); |
334 |
| - matchers.add(new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext)); |
335 |
| - } |
336 |
| - return requestMatchers(matchers.toArray(new RequestMatcher[0])); |
| 359 | + return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext); |
337 | 360 | }
|
338 | 361 | dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
339 | 362 | if (dispatcherServlet != null) {
|
340 | 363 | String mapping = dispatcherServlet.getMappings().iterator().next();
|
341 |
| - List<MvcRequestMatcher> matchers = createMvcMatchers(method, patterns); |
342 |
| - for (MvcRequestMatcher matcher : matchers) { |
343 |
| - matcher.setServletPath(mapping.substring(0, mapping.length() - 2)); |
344 |
| - } |
345 |
| - return requestMatchers(matchers.toArray(new RequestMatcher[0])); |
| 364 | + mvc.setServletPath(mapping.substring(0, mapping.length() - 2)); |
| 365 | + return mvc; |
346 | 366 | }
|
347 | 367 | String errorMessage = computeErrorMessage(registrations.values());
|
348 | 368 | throw new IllegalArgumentException(errorMessage);
|
@@ -562,6 +582,38 @@ static List<RequestMatcher> regexMatchers(String... regexPatterns) {
|
562 | 582 |
|
563 | 583 | }
|
564 | 584 |
|
| 585 | + static class DeferredRequestMatcher implements RequestMatcher { |
| 586 | + |
| 587 | + final Supplier<RequestMatcher> requestMatcher; |
| 588 | + |
| 589 | + final AtomicReference<String> description = new AtomicReference<>(); |
| 590 | + |
| 591 | + DeferredRequestMatcher(Supplier<RequestMatcher> resolver, RequestMatcher... candidates) { |
| 592 | + this.requestMatcher = SingletonSupplier.of(() -> { |
| 593 | + RequestMatcher matcher = resolver.get(); |
| 594 | + this.description.set(matcher.toString()); |
| 595 | + return matcher; |
| 596 | + }); |
| 597 | + this.description.set("Deferred " + candidates); |
| 598 | + } |
| 599 | + |
| 600 | + @Override |
| 601 | + public boolean matches(HttpServletRequest request) { |
| 602 | + return this.requestMatcher.get().matches(request); |
| 603 | + } |
| 604 | + |
| 605 | + @Override |
| 606 | + public MatchResult matcher(HttpServletRequest request) { |
| 607 | + return this.requestMatcher.get().matcher(request); |
| 608 | + } |
| 609 | + |
| 610 | + @Override |
| 611 | + public String toString() { |
| 612 | + return this.description.get(); |
| 613 | + } |
| 614 | + |
| 615 | + } |
| 616 | + |
565 | 617 | static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
|
566 | 618 |
|
567 | 619 | private final AntPathRequestMatcher ant;
|
@@ -611,6 +663,11 @@ private boolean isDispatcherServlet(ServletRegistration registration) {
|
611 | 663 | }
|
612 | 664 | }
|
613 | 665 |
|
| 666 | + @Override |
| 667 | + public String toString() { |
| 668 | + return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]"; |
| 669 | + } |
| 670 | + |
614 | 671 | }
|
615 | 672 |
|
616 | 673 | }
|
0 commit comments