Skip to content

Commit 3ab235c

Browse files
committed
Merge branch '6.1.x'
2 parents b9b2df8 + 6417228 commit 3ab235c

File tree

3 files changed

+59
-16
lines changed

3 files changed

+59
-16
lines changed

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

+27-12
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@
2323
import java.util.List;
2424
import java.util.Map;
2525
import java.util.concurrent.atomic.AtomicReference;
26-
import java.util.function.Supplier;
26+
import java.util.function.Function;
2727

2828
import jakarta.servlet.DispatcherType;
2929
import jakarta.servlet.ServletContext;
3030
import jakarta.servlet.ServletRegistration;
3131
import jakarta.servlet.http.HttpServletRequest;
32+
import org.apache.commons.logging.Log;
33+
import org.apache.commons.logging.LogFactory;
3234

3335
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
3436
import org.springframework.context.ApplicationContext;
@@ -44,7 +46,6 @@
4446
import org.springframework.security.web.util.matcher.RequestMatcher;
4547
import org.springframework.util.Assert;
4648
import org.springframework.util.ClassUtils;
47-
import org.springframework.util.function.SingletonSupplier;
4849
import org.springframework.web.context.WebApplicationContext;
4950
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
5051

@@ -76,6 +77,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
7677
AbstractRequestMatcherRegistry.class.getClassLoader());
7778
}
7879

80+
private final Log logger = LogFactory.getLog(getClass());
81+
7982
protected final void setApplicationContext(ApplicationContext context) {
8083
this.context = context;
8184
}
@@ -209,7 +212,12 @@ public C requestMatchers(HttpMethod method, String... patterns) {
209212
matchers.add(resolve(ant, mvc, servletContext));
210213
}
211214
else {
212-
matchers.add(new DeferredRequestMatcher(() -> resolve(ant, mvc, servletContext), mvc, ant));
215+
this.logger
216+
.warn("The ServletRegistration API was not available at startup time. This may be due to a misconfiguration; "
217+
+ "if you are using AbstractSecurityWebApplicationInitializer, please double-check the recommendations outlined in "
218+
+ "https://docs.spring.io/spring-security/reference/servlet/configuration/java.html#abstractsecuritywebapplicationinitializer-with-spring-mvc");
219+
matchers.add(new DeferredRequestMatcher((request) -> resolve(ant, mvc, request.getServletContext()),
220+
mvc, ant));
213221
}
214222
}
215223
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
@@ -466,27 +474,34 @@ static List<RequestMatcher> regexMatchers(String... regexPatterns) {
466474

467475
static class DeferredRequestMatcher implements RequestMatcher {
468476

469-
final Supplier<RequestMatcher> requestMatcher;
477+
final Function<HttpServletRequest, RequestMatcher> requestMatcherFactory;
470478

471479
final AtomicReference<String> description = new AtomicReference<>();
472480

473-
DeferredRequestMatcher(Supplier<RequestMatcher> resolver, RequestMatcher... candidates) {
474-
this.requestMatcher = SingletonSupplier.of(() -> {
475-
RequestMatcher matcher = resolver.get();
476-
this.description.set(matcher.toString());
477-
return matcher;
478-
});
481+
volatile RequestMatcher requestMatcher;
482+
483+
DeferredRequestMatcher(Function<HttpServletRequest, RequestMatcher> resolver, RequestMatcher... candidates) {
484+
this.requestMatcherFactory = (request) -> {
485+
if (this.requestMatcher == null) {
486+
synchronized (this) {
487+
if (this.requestMatcher == null) {
488+
this.requestMatcher = resolver.apply(request);
489+
}
490+
}
491+
}
492+
return this.requestMatcher;
493+
};
479494
this.description.set("Deferred " + Arrays.toString(candidates));
480495
}
481496

482497
@Override
483498
public boolean matches(HttpServletRequest request) {
484-
return this.requestMatcher.get().matches(request);
499+
return this.requestMatcherFactory.apply(request).matches(request);
485500
}
486501

487502
@Override
488503
public MatchResult matcher(HttpServletRequest request) {
489-
return this.requestMatcher.get().matcher(request);
504+
return this.requestMatcherFactory.apply(request).matcher(request);
490505
}
491506

492507
@Override

config/src/test/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistryTests.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ private static List<RequestMatcher> unwrap(List<RequestMatcher> wrappedMatchers)
307307
List<RequestMatcher> requestMatchers = new ArrayList<>();
308308
for (RequestMatcher requestMatcher : wrappedMatchers) {
309309
if (requestMatcher instanceof AbstractRequestMatcherRegistry.DeferredRequestMatcher) {
310-
requestMatchers.add(((DeferredRequestMatcher) requestMatcher).requestMatcher.get());
310+
requestMatchers.add(((DeferredRequestMatcher) requestMatcher).requestMatcher);
311311
}
312312
else {
313313
requestMatchers.add(requestMatcher);

docs/modules/ROOT/pages/servlet/configuration/java.adoc

+31-3
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public class SecurityWebApplicationInitializer
114114

115115
This onlys register the `springSecurityFilterChain` for every URL in your application.
116116
After that, we need to ensure that `WebSecurityConfig` was loaded in our existing `ApplicationInitializer`.
117-
For example, if we use Spring MVC it is added in the `getRootConfigClasses()`:
117+
For example, if we use Spring MVC it is added in the `getServletConfigClasses()`:
118118

119119
[[message-web-application-inititializer-java]]
120120
[source,java]
@@ -123,14 +123,42 @@ public class MvcWebApplicationInitializer extends
123123
AbstractAnnotationConfigDispatcherServletInitializer {
124124
125125
@Override
126-
protected Class<?>[] getRootConfigClasses() {
127-
return new Class[] { WebSecurityConfig.class };
126+
protected Class<?>[] getServletConfigClasses() {
127+
return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
128128
}
129129
130130
// ... other overrides ...
131131
}
132132
----
133133

134+
The reason for this is that Spring Security needs to be able to inspect some Spring MVC configuration in order to appropriately configure xref:servlet/authorization/authorize-http-requests.adoc#_request_matchers[underlying request matchers], so they need to be in the same application context.
135+
Placing Spring Security in `getRootConfigClasses` places it into a parent application context that may not be able to find Spring MVC's `HandlerMappingIntrospector`.
136+
137+
==== Configuring for Multiple Spring MVC Dispatchers
138+
139+
If desired, any Spring Security configuration that is unrelated to Spring MVC may be placed in a different configuration class like so:
140+
141+
[source,java]
142+
----
143+
public class MvcWebApplicationInitializer extends
144+
AbstractAnnotationConfigDispatcherServletInitializer {
145+
146+
@Override
147+
protected Class<?>[] getRootConfigClasses() {
148+
return new Class[] { NonWebSecurityConfig.class };
149+
}
150+
151+
@Override
152+
protected Class<?>[] getServletConfigClasses() {
153+
return new Class[] { WebSecurityConfig.class, WebMvcConfig.class };
154+
}
155+
156+
// ... other overrides ...
157+
}
158+
----
159+
160+
This can be helpful if you have multiple instances of `AbstractAnnotationConfigDispatcherServletInitializer` and don't want to duplicate the general security configuration across both of them.
161+
134162
[[jc-httpsecurity]]
135163
== HttpSecurity
136164

0 commit comments

Comments
 (0)