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
This commit is contained in:
parent
5a798e93f1
commit
cdd626644e
|
@ -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;
|
||||||
|
|
||||||
|
@ -322,36 +323,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()) {
|
||||||
|
@ -593,34 +573,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
|
||||||
|
|
|
@ -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.
|
||||||
|
@ -381,11 +381,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);
|
||||||
|
|
Loading…
Reference in New Issue