diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java index 996a6741ee..8dc4cbae0b 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -322,36 +323,15 @@ public abstract class AbstractRequestMatcherRegistry { if (servletContext == null) { return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); } - boolean isProgrammaticApiAvailable = isProgrammaticApiAvailable(servletContext); List matchers = new ArrayList<>(); for (String pattern : patterns) { AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null); MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0); - if (isProgrammaticApiAvailable) { - matchers.add(resolve(ant, mvc, servletContext)); - } - else { - this.logger - .warn("The ServletRegistration API was not available at startup time. This may be due to a misconfiguration; " - + "if you are using AbstractSecurityWebApplicationInitializer, please double-check the recommendations outlined in " - + "https://docs.spring.io/spring-security/reference/servlet/configuration/java.html#abstractsecuritywebapplicationinitializer-with-spring-mvc"); - matchers.add(new DeferredRequestMatcher((request) -> resolve(ant, mvc, request.getServletContext()), - mvc, ant)); - } + matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant)); } return requestMatchers(matchers.toArray(new RequestMatcher[0])); } - private static boolean isProgrammaticApiAvailable(ServletContext servletContext) { - try { - servletContext.getServletRegistrations(); - return true; - } - catch (UnsupportedOperationException ex) { - return false; - } - } - private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) { Map registrations = mappableServletRegistrations(servletContext); if (registrations.isEmpty()) { @@ -593,34 +573,29 @@ public abstract class AbstractRequestMatcherRegistry { static class DeferredRequestMatcher implements RequestMatcher { - final Function requestMatcherFactory; + final Function requestMatcherFactory; final AtomicReference description = new AtomicReference<>(); - volatile RequestMatcher requestMatcher; + final Map requestMatchers = new ConcurrentHashMap<>(); - DeferredRequestMatcher(Function resolver, RequestMatcher... candidates) { - this.requestMatcherFactory = (request) -> { - if (this.requestMatcher == null) { - synchronized (this) { - if (this.requestMatcher == null) { - this.requestMatcher = resolver.apply(request); - } - } - } - return this.requestMatcher; - }; + DeferredRequestMatcher(Function resolver, RequestMatcher... candidates) { + this.requestMatcherFactory = (sc) -> this.requestMatchers.computeIfAbsent(sc, resolver); this.description.set("Deferred " + Arrays.toString(candidates)); } + RequestMatcher requestMatcher(ServletContext servletContext) { + return this.requestMatcherFactory.apply(servletContext); + } + @Override public boolean matches(HttpServletRequest request) { - return this.requestMatcherFactory.apply(request).matches(request); + return this.requestMatcherFactory.apply(request.getServletContext()).matches(request); } @Override public MatchResult matcher(HttpServletRequest request) { - return this.requestMatcherFactory.apply(request).matcher(request); + return this.requestMatcherFactory.apply(request.getServletContext()).matcher(request); } @Override diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java index 0ebbdb5ba7..e5f96703ba 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -381,11 +381,13 @@ public class AbstractRequestMatcherRegistryTests { return requestMatchers; } - private static List unwrap(List wrappedMatchers) { + private List unwrap(List wrappedMatchers) { List requestMatchers = new ArrayList<>(); for (RequestMatcher requestMatcher : wrappedMatchers) { - if (requestMatcher instanceof AbstractRequestMatcherRegistry.DeferredRequestMatcher) { - requestMatchers.add(((DeferredRequestMatcher) requestMatcher).requestMatcher); + if (requestMatcher instanceof DeferredRequestMatcher) { + DeferredRequestMatcher deferred = (DeferredRequestMatcher) requestMatcher; + WebApplicationContext web = (WebApplicationContext) getApplicationContext(); + requestMatchers.add(deferred.requestMatcher(web.getServletContext())); } else { requestMatchers.add(requestMatcher);