Skip to content

Commit 75a3579

Browse files
committed
Polish requestMatchers Logic
Issue gh-13551
1 parent ddca7dc commit 75a3579

File tree

2 files changed

+95
-89
lines changed

2 files changed

+95
-89
lines changed

config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java

+18-89
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.http.HttpMethod;
4141
import org.springframework.lang.Nullable;
4242
import org.springframework.security.config.ObjectPostProcessor;
43+
import org.springframework.security.config.annotation.web.ServletRegistrationsSupport.RegistrationMapping;
4344
import org.springframework.security.config.annotation.web.configurers.AbstractConfigAttributeRequestMatcherRegistry;
4445
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
4546
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@@ -235,103 +236,31 @@ private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
235236
}
236237

237238
private RequestMatcher resolve(AntPathRequestMatcher ant, MvcRequestMatcher mvc, ServletContext servletContext) {
238-
Map<String, ? extends ServletRegistration> registrations = mappableServletRegistrations(servletContext);
239-
if (registrations.isEmpty()) {
239+
ServletRegistrationsSupport registrations = new ServletRegistrationsSupport(servletContext);
240+
Collection<RegistrationMapping> mappings = registrations.mappings();
241+
if (mappings.isEmpty()) {
240242
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
241243
}
242-
if (!hasDispatcherServlet(registrations)) {
244+
Collection<RegistrationMapping> dispatcherServletMappings = registrations.dispatcherServletMappings();
245+
if (dispatcherServletMappings.isEmpty()) {
243246
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, new MockMvcRequestMatcher());
244247
}
245-
ServletRegistration dispatcherServlet = requireOneRootDispatcherServlet(registrations);
246-
if (dispatcherServlet != null) {
247-
if (registrations.size() == 1) {
248-
return mvc;
249-
}
250-
return new DispatcherServletDelegatingRequestMatcher(ant, mvc, servletContext);
248+
if (dispatcherServletMappings.size() > 1) {
249+
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
250+
throw new IllegalArgumentException(errorMessage);
251251
}
252-
dispatcherServlet = requireOnlyPathMappedDispatcherServlet(registrations);
253-
if (dispatcherServlet != null) {
254-
String mapping = dispatcherServlet.getMappings().iterator().next();
255-
mvc.setServletPath(mapping.substring(0, mapping.length() - 2));
256-
return mvc;
257-
}
258-
String errorMessage = computeErrorMessage(registrations.values());
259-
throw new IllegalArgumentException(errorMessage);
260-
}
261-
262-
private Map<String, ? extends ServletRegistration> mappableServletRegistrations(ServletContext servletContext) {
263-
Map<String, ServletRegistration> mappable = new LinkedHashMap<>();
264-
for (Map.Entry<String, ? extends ServletRegistration> entry : servletContext.getServletRegistrations()
265-
.entrySet()) {
266-
if (!entry.getValue().getMappings().isEmpty()) {
267-
mappable.put(entry.getKey(), entry.getValue());
268-
}
252+
RegistrationMapping dispatcherServlet = dispatcherServletMappings.iterator().next();
253+
if (mappings.size() > 1 && !dispatcherServlet.isDefault()) {
254+
String errorMessage = computeErrorMessage(servletContext.getServletRegistrations().values());
255+
throw new IllegalArgumentException(errorMessage);
269256
}
270-
return mappable;
271-
}
272-
273-
private boolean hasDispatcherServlet(Map<String, ? extends ServletRegistration> registrations) {
274-
if (registrations == null) {
275-
return false;
276-
}
277-
for (ServletRegistration registration : registrations.values()) {
278-
if (isDispatcherServlet(registration)) {
279-
return true;
280-
}
281-
}
282-
return false;
283-
}
284-
285-
private ServletRegistration requireOneRootDispatcherServlet(
286-
Map<String, ? extends ServletRegistration> registrations) {
287-
ServletRegistration rootDispatcherServlet = null;
288-
for (ServletRegistration registration : registrations.values()) {
289-
if (!isDispatcherServlet(registration)) {
290-
continue;
291-
}
292-
if (registration.getMappings().size() > 1) {
293-
return null;
294-
}
295-
if (!"/".equals(registration.getMappings().iterator().next())) {
296-
return null;
297-
}
298-
rootDispatcherServlet = registration;
299-
}
300-
return rootDispatcherServlet;
301-
}
302-
303-
private ServletRegistration requireOnlyPathMappedDispatcherServlet(
304-
Map<String, ? extends ServletRegistration> registrations) {
305-
ServletRegistration pathDispatcherServlet = null;
306-
for (ServletRegistration registration : registrations.values()) {
307-
if (!isDispatcherServlet(registration)) {
308-
return null;
309-
}
310-
if (registration.getMappings().size() > 1) {
311-
return null;
312-
}
313-
String mapping = registration.getMappings().iterator().next();
314-
if (!mapping.startsWith("/") || !mapping.endsWith("/*")) {
315-
return null;
316-
}
317-
if (pathDispatcherServlet != null) {
318-
return null;
257+
if (dispatcherServlet.isDefault()) {
258+
if (mappings.size() == 1) {
259+
return mvc;
319260
}
320-
pathDispatcherServlet = registration;
321-
}
322-
return pathDispatcherServlet;
323-
}
324-
325-
private boolean isDispatcherServlet(ServletRegistration registration) {
326-
Class<?> dispatcherServlet = ClassUtils.resolveClassName("org.springframework.web.servlet.DispatcherServlet",
327-
null);
328-
try {
329-
Class<?> clazz = Class.forName(registration.getClassName());
330-
return dispatcherServlet.isAssignableFrom(clazz);
331-
}
332-
catch (ClassNotFoundException ex) {
333-
return false;
261+
return new DispatcherServletDelegatingRequestMatcher(ant, mvc);
334262
}
263+
return mvc;
335264
}
336265

337266
private static String computeErrorMessage(Collection<? extends ServletRegistration> registrations) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2002-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.config.annotation.web;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.Map;
22+
23+
import jakarta.servlet.ServletContext;
24+
import jakarta.servlet.ServletRegistration;
25+
26+
import org.springframework.util.ClassUtils;
27+
28+
class ServletRegistrationsSupport {
29+
30+
private final Collection<RegistrationMapping> registrations;
31+
32+
ServletRegistrationsSupport(ServletContext servletContext) {
33+
Map<String, ? extends ServletRegistration> registrations = servletContext.getServletRegistrations();
34+
Collection<RegistrationMapping> mappings = new ArrayList<>();
35+
for (Map.Entry<String, ? extends ServletRegistration> entry : registrations.entrySet()) {
36+
if (!entry.getValue().getMappings().isEmpty()) {
37+
for (String mapping : entry.getValue().getMappings()) {
38+
mappings.add(new RegistrationMapping(entry.getValue(), mapping));
39+
}
40+
}
41+
}
42+
this.registrations = mappings;
43+
}
44+
45+
Collection<RegistrationMapping> dispatcherServletMappings() {
46+
Collection<RegistrationMapping> mappings = new ArrayList<>();
47+
for (RegistrationMapping registration : this.registrations) {
48+
if (registration.isDispatcherServlet()) {
49+
mappings.add(registration);
50+
}
51+
}
52+
return mappings;
53+
}
54+
55+
Collection<RegistrationMapping> mappings() {
56+
return this.registrations;
57+
}
58+
59+
record RegistrationMapping(ServletRegistration registration, String mapping) {
60+
boolean isDispatcherServlet() {
61+
Class<?> dispatcherServlet = ClassUtils
62+
.resolveClassName("org.springframework.web.servlet.DispatcherServlet", null);
63+
try {
64+
Class<?> clazz = Class.forName(this.registration.getClassName());
65+
return dispatcherServlet.isAssignableFrom(clazz);
66+
}
67+
catch (ClassNotFoundException ex) {
68+
return false;
69+
}
70+
}
71+
72+
boolean isDefault() {
73+
return "/".equals(this.mapping);
74+
}
75+
}
76+
77+
}

0 commit comments

Comments
 (0)