浏览代码

Use Request-Level Servlet Context

Spring Security cannot use the ServletContext attached
to the ApplicationContext since there may be child
ApplicationContext's with their own ServletContext.

Because of that, it is necessary to always use the
ServletContext attached to the request.

Closes gh-14418
Josh Cummings 1 年之前
父节点
当前提交
cdd626644e

+ 14 - 39
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<C> {
 		if (servletContext == null) {
 			return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
 		}
-		boolean isProgrammaticApiAvailable = isProgrammaticApiAvailable(servletContext);
 		List<RequestMatcher> 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<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
 		if (registrations.isEmpty()) {
@@ -593,34 +573,29 @@ public abstract class AbstractRequestMatcherRegistry<C> {
 
 	static class DeferredRequestMatcher implements RequestMatcher {
 
-		final Function<HttpServletRequest, RequestMatcher> requestMatcherFactory;
+		final Function<ServletContext, RequestMatcher> requestMatcherFactory;
 
 		final AtomicReference<String> description = new AtomicReference<>();
 
-		volatile RequestMatcher requestMatcher;
-
-		DeferredRequestMatcher(Function<HttpServletRequest, RequestMatcher> resolver, RequestMatcher... candidates) {
-			this.requestMatcherFactory = (request) -> {
-				if (this.requestMatcher == null) {
-					synchronized (this) {
-						if (this.requestMatcher == null) {
-							this.requestMatcher = resolver.apply(request);
-						}
-					}
-				}
-				return this.requestMatcher;
-			};
+		final Map<ServletContext, RequestMatcher> requestMatchers = new ConcurrentHashMap<>();
+
+		DeferredRequestMatcher(Function<ServletContext, RequestMatcher> 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

+ 6 - 4
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<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) {
+		private List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) {
 			List<RequestMatcher> 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);