commit
aa1a022605
|
@ -22,6 +22,8 @@ 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.atomic.AtomicReference;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
import jakarta.servlet.ServletContext;
|
import jakarta.servlet.ServletContext;
|
||||||
|
@ -42,6 +44,7 @@ import org.springframework.security.web.util.matcher.RegexRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.ClassUtils;
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.function.SingletonSupplier;
|
||||||
import org.springframework.web.context.WebApplicationContext;
|
import org.springframework.web.context.WebApplicationContext;
|
||||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||||
|
|
||||||
|
@ -197,34 +200,51 @@ 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<>();
|
||||||
|
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 {
|
||||||
|
matchers.add(new DeferredRequestMatcher(() -> resolve(ant, mvc, servletContext), 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);
|
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
|
||||||
if (registrations.isEmpty()) {
|
if (registrations.isEmpty()) {
|
||||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
return ant;
|
||||||
}
|
}
|
||||||
if (!hasDispatcherServlet(registrations)) {
|
if (!hasDispatcherServlet(registrations)) {
|
||||||
return requestMatchers(RequestMatchers.antMatchersAsArray(method, patterns));
|
return ant;
|
||||||
}
|
}
|
||||||
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
|
||||||
if (dispatcherServlet != null) {
|
if (dispatcherServlet != null) {
|
||||||
if (registrations.size() == 1) {
|
if (registrations.size() == 1) {
|
||||||
return requestMatchers(createMvcMatchers(method, patterns).toArray(new RequestMatcher[0]));
|
return mvc;
|
||||||
}
|
}
|
||||||
List<RequestMatcher> matchers = new ArrayList<>();
|
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
|
||||||
for (String pattern : patterns) {
|
|
||||||
AntPathRequestMatcher ant = new AntPathRequestMatcher(pattern, (method != null) ? method.name() : null);
|
|
||||||
MvcRequestMatcher mvc = createMvcMatchers(method, pattern).get(0);
|
|
||||||
matchers.add(new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext));
|
|
||||||
}
|
|
||||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
|
||||||
}
|
}
|
||||||
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
|
||||||
if (dispatcherServlet != null) {
|
if (dispatcherServlet != null) {
|
||||||
String mapping = dispatcherServlet.getMappings().iterator().next();
|
String mapping = dispatcherServlet.getMappings().iterator().next();
|
||||||
List<MvcRequestMatcher> matchers = createMvcMatchers(method, patterns);
|
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
|
||||||
for (MvcRequestMatcher matcher : matchers) {
|
return mvc;
|
||||||
matcher.setServletPath(mapping.substring(0, mapping.length() - 2));
|
|
||||||
}
|
|
||||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
|
||||||
}
|
}
|
||||||
String errorMessage = computeErrorMessage(registrations.values());
|
String errorMessage = computeErrorMessage(registrations.values());
|
||||||
throw new IllegalArgumentException(errorMessage);
|
throw new IllegalArgumentException(errorMessage);
|
||||||
|
@ -444,6 +464,38 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class DeferredRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
|
final Supplier<RequestMatcher> requestMatcher;
|
||||||
|
|
||||||
|
final AtomicReference<String> description = new AtomicReference<>();
|
||||||
|
|
||||||
|
DeferredRequestMatcher(Supplier<RequestMatcher> resolver, RequestMatcher... candidates) {
|
||||||
|
this.requestMatcher = SingletonSupplier.of(() -> {
|
||||||
|
RequestMatcher matcher = resolver.get();
|
||||||
|
this.description.set(matcher.toString());
|
||||||
|
return matcher;
|
||||||
|
});
|
||||||
|
this.description.set("Deferred " + candidates);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(HttpServletRequest request) {
|
||||||
|
return this.requestMatcher.get().matches(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MatchResult matcher(HttpServletRequest request) {
|
||||||
|
return this.requestMatcher.get().matcher(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.description.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
|
static class DispatcherServletDelegatingRequestMatcher implements RequestMatcher {
|
||||||
|
|
||||||
private final AntPathRequestMatcher ant;
|
private final AntPathRequestMatcher ant;
|
||||||
|
@ -493,6 +545,11 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "DispatcherServletDelegating [" + "ant = " + this.ant + ", mvc = " + this.mvc + "]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web;
|
package org.springframework.security.config.annotation.web;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
|
@ -164,6 +165,7 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() {
|
public void requestMatchersWhenNoDispatcherServletThenAntPathRequestMatcherType() {
|
||||||
|
mockMvcIntrospector(true);
|
||||||
MockServletContext servletContext = new MockServletContext();
|
MockServletContext servletContext = new MockServletContext();
|
||||||
given(this.context.getServletContext()).willReturn(servletContext);
|
given(this.context.getServletContext()).willReturn(servletContext);
|
||||||
servletContext.addServlet("servletOne", Servlet.class).addMapping("/one");
|
servletContext.addServlet("servletOne", Servlet.class).addMapping("/one");
|
||||||
|
@ -182,6 +184,7 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersWhenAmbiguousServletsThenException() {
|
public void requestMatchersWhenAmbiguousServletsThenException() {
|
||||||
|
mockMvcIntrospector(true);
|
||||||
MockServletContext servletContext = new MockServletContext();
|
MockServletContext servletContext = new MockServletContext();
|
||||||
given(this.context.getServletContext()).willReturn(servletContext);
|
given(this.context.getServletContext()).willReturn(servletContext);
|
||||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
|
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/");
|
||||||
|
@ -192,6 +195,7 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersWhenMultipleDispatcherServletMappingsThenException() {
|
public void requestMatchersWhenMultipleDispatcherServletMappingsThenException() {
|
||||||
|
mockMvcIntrospector(true);
|
||||||
MockServletContext servletContext = new MockServletContext();
|
MockServletContext servletContext = new MockServletContext();
|
||||||
given(this.context.getServletContext()).willReturn(servletContext);
|
given(this.context.getServletContext()).willReturn(servletContext);
|
||||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/mvc/*");
|
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/", "/mvc/*");
|
||||||
|
@ -201,6 +205,7 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void requestMatchersWhenPathDispatcherServletAndOtherServletsThenException() {
|
public void requestMatchersWhenPathDispatcherServletAndOtherServletsThenException() {
|
||||||
|
mockMvcIntrospector(true);
|
||||||
MockServletContext servletContext = new MockServletContext();
|
MockServletContext servletContext = new MockServletContext();
|
||||||
given(this.context.getServletContext()).willReturn(servletContext);
|
given(this.context.getServletContext()).willReturn(servletContext);
|
||||||
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*");
|
servletContext.addServlet("dispatcherServlet", DispatcherServlet.class).addMapping("/mvc/*");
|
||||||
|
@ -309,11 +314,29 @@ public class AbstractRequestMatcherRegistryTests {
|
||||||
|
|
||||||
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
|
private static class TestRequestMatcherRegistry extends AbstractRequestMatcherRegistry<List<RequestMatcher>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<RequestMatcher> requestMatchers(RequestMatcher... requestMatchers) {
|
||||||
|
return unwrap(super.requestMatchers(requestMatchers));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
protected List<RequestMatcher> chainRequestMatchers(List<RequestMatcher> requestMatchers) {
|
||||||
return requestMatchers;
|
return requestMatchers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static 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.get());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
requestMatchers.add(requestMatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return requestMatchers;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue