Merge branch '6.3.x'

This commit is contained in:
Josh Cummings 2024-06-03 17:43:12 -06:00
commit 81abc453fe
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
5 changed files with 25 additions and 48 deletions

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with 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.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function; import java.util.function.Function;
@ -203,36 +204,15 @@ public abstract class AbstractRequestMatcherRegistry<C> {
if (servletContext == null) { if (servletContext == null) {
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns)); return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
} }
boolean isProgrammaticApiAvailable = isProgrammaticApiAvailable(servletContext);
List<RequestMatcher> matchers = new ArrayList<>(); List<RequestMatcher> matchers = new ArrayList<>();
for (String pattern : patterns) { for (String pattern : patterns) {
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null); AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0); MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
if (isProgrammaticApiAvailable) { matchers.add(new DeferredRequestMatcher((c) -> resolve(ant, mvc, c), mvc, ant));
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));
}
} }
return requestMatchers(matchers.toArray(new RequestMatcher[0])); 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) { private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext); Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
if (registrations.isEmpty()) { if (registrations.isEmpty()) {
@ -474,34 +454,29 @@ public abstract class AbstractRequestMatcherRegistry<C> {
static class DeferredRequestMatcher implements RequestMatcher { static class DeferredRequestMatcher implements RequestMatcher {
final Function<HttpServletRequest, RequestMatcher> requestMatcherFactory; final Function<ServletContext, RequestMatcher> requestMatcherFactory;
final AtomicReference<String> description = new AtomicReference<>(); final AtomicReference<String> description = new AtomicReference<>();
volatile RequestMatcher requestMatcher; final Map<ServletContext, RequestMatcher> requestMatchers = new ConcurrentHashMap<>();
DeferredRequestMatcher(Function<HttpServletRequest, RequestMatcher> resolver, RequestMatcher... candidates) { DeferredRequestMatcher(Function<ServletContext, RequestMatcher> resolver, RequestMatcher... candidates) {
this.requestMatcherFactory = (request) -> { this.requestMatcherFactory = (sc) -> this.requestMatchers.computeIfAbsent(sc, resolver);
if (this.requestMatcher == null) {
synchronized (this) {
if (this.requestMatcher == null) {
this.requestMatcher = resolver.apply(request);
}
}
}
return this.requestMatcher;
};
this.description.set("Deferred " + Arrays.toString(candidates)); this.description.set("Deferred " + Arrays.toString(candidates));
} }
RequestMatcher requestMatcher(ServletContext servletContext) {
return this.requestMatcherFactory.apply(servletContext);
}
@Override @Override
public boolean matches(HttpServletRequest request) { public boolean matches(HttpServletRequest request) {
return this.requestMatcherFactory.apply(request).matches(request); return this.requestMatcherFactory.apply(request.getServletContext()).matches(request);
} }
@Override @Override
public MatchResult matcher(HttpServletRequest request) { public MatchResult matcher(HttpServletRequest request) {
return this.requestMatcherFactory.apply(request).matcher(request); return this.requestMatcherFactory.apply(request.getServletContext()).matcher(request);
} }
@Override @Override

View File

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -303,11 +303,13 @@ public class AbstractRequestMatcherRegistryTests {
return requestMatchers; return requestMatchers;
} }
private static List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) { private List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers) {
List<RequestMatcher> requestMatchers = new ArrayList<>(); List<RequestMatcher> requestMatchers = new ArrayList<>();
for (RequestMatcher requestMatcher : wrappedMatchers) { for (RequestMatcher requestMatcher : wrappedMatchers) {
if (requestMatcher instanceof AbstractRequestMatcherRegistry.DeferredRequestMatcher) { if (requestMatcher instanceof DeferredRequestMatcher) {
requestMatchers.add(((DeferredRequestMatcher) requestMatcher).requestMatcher); DeferredRequestMatcher deferred = (DeferredRequestMatcher) requestMatcher;
WebApplicationContext web = (WebApplicationContext) getApplicationContext();
requestMatchers.add(deferred.requestMatcher(web.getServletContext()));
} }
else { else {
requestMatchers.add(requestMatcher); requestMatchers.add(requestMatcher);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -78,7 +78,7 @@ public class AuthorizeRequestsTests {
@BeforeEach @BeforeEach
public void setup() { public void setup() {
this.servletContext = spy(MockServletContext.mvc()); this.servletContext = spy(MockServletContext.mvc());
this.request = new MockHttpServletRequest("GET", ""); this.request = new MockHttpServletRequest(this.servletContext, "GET", "");
this.request.setMethod("GET"); this.request.setMethod("GET");
this.response = new MockHttpServletResponse(); this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain(); this.chain = new MockFilterChain();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -75,7 +75,7 @@ public class HttpSecuritySecurityMatchersTests {
@BeforeEach @BeforeEach
public void setup() throws Exception { public void setup() throws Exception {
this.request = new MockHttpServletRequest("GET", ""); this.request = new MockHttpServletRequest(MockServletContext.mvc(), "GET", "");
this.request.setMethod("GET"); this.request.setMethod("GET");
this.response = new MockHttpServletResponse(); this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain(); this.chain = new MockFilterChain();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2022 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -72,7 +72,7 @@ public class UrlAuthorizationConfigurerTests {
@BeforeEach @BeforeEach
public void setup() { public void setup() {
this.request = new MockHttpServletRequest("GET", ""); this.request = new MockHttpServletRequest(MockServletContext.mvc(), "GET", "");
this.request.setMethod("GET"); this.request.setMethod("GET");
this.response = new MockHttpServletResponse(); this.response = new MockHttpServletResponse();
this.chain = new MockFilterChain(); this.chain = new MockFilterChain();