Skip to content

Commit aeb2dbc

Browse files
committed
Move PathPatternRequestMatcher.Builder to Shared Object
This commit changes the DSL to look for a shared object instead of publishing a bean for PathPatternRequestMatcher.Builder. Closes gh-17746
1 parent 006f638 commit aeb2dbc

File tree

10 files changed

+119
-51
lines changed

10 files changed

+119
-51
lines changed

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@
2727
import org.springframework.context.ApplicationContext;
2828
import org.springframework.http.HttpMethod;
2929
import org.springframework.lang.Nullable;
30+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
3031
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
3132
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
3233
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
3334
import org.springframework.security.web.util.matcher.RequestMatcher;
3435
import org.springframework.util.Assert;
36+
import org.springframework.util.function.ThrowingSupplier;
3537

3638
/**
3739
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
@@ -52,6 +54,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
5254

5355
private final Log logger = LogFactory.getLog(getClass());
5456

57+
private PathPatternRequestMatcher.Builder requestMatcherBuilder;
58+
5559
protected final void setApplicationContext(ApplicationContext context) {
5660
this.context = context;
5761
}
@@ -140,14 +144,31 @@ public C requestMatchers(HttpMethod method, String... patterns) {
140144
+ "Spring Security, leaving out the leading slash will result in an exception.");
141145
}
142146
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
143-
PathPatternRequestMatcher.Builder builder = this.context.getBean(PathPatternRequestMatcher.Builder.class);
147+
PathPatternRequestMatcher.Builder builder = getRequestMatcherBuilder();
144148
List<RequestMatcher> matchers = new ArrayList<>();
145149
for (String pattern : patterns) {
146150
matchers.add(builder.matcher(method, pattern));
147151
}
148152
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
149153
}
150154

155+
private PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
156+
if (this.requestMatcherBuilder != null) {
157+
return this.requestMatcherBuilder;
158+
}
159+
this.requestMatcherBuilder = this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
160+
.getIfUnique(() -> constructRequestMatcherBuilder(this.context));
161+
return this.requestMatcherBuilder;
162+
}
163+
164+
private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
165+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
166+
requestMatcherBuilder.setApplicationContext(context);
167+
requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
168+
requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
169+
return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
170+
}
171+
151172
private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
152173
for (String pattern : patterns) {
153174
if (!pattern.startsWith("/")) {

config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2058,7 +2058,7 @@ public HttpSecurity securityMatcher(RequestMatcher requestMatcher) {
20582058
*/
20592059
public HttpSecurity securityMatcher(String... patterns) {
20602060
List<RequestMatcher> matchers = new ArrayList<>();
2061-
PathPatternRequestMatcher.Builder builder = getContext().getBean(PathPatternRequestMatcher.Builder.class);
2061+
PathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);
20622062
for (String pattern : patterns) {
20632063
matchers.add(builder.matcher(pattern));
20642064
}

config/src/main/java/org/springframework/security/config/annotation/web/configuration/AuthorizationConfiguration.java

Lines changed: 0 additions & 31 deletions
This file was deleted.

config/src/main/java/org/springframework/security/config/annotation/web/configuration/EnableWebSecurity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
@Target(ElementType.TYPE)
8484
@Documented
8585
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
86-
HttpSecurityConfiguration.class, ObservationImportSelector.class, AuthorizationConfiguration.class })
86+
HttpSecurityConfiguration.class, ObservationImportSelector.class })
8787
@EnableGlobalAuthentication
8888
public @interface EnableWebSecurity {
8989

config/src/main/java/org/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,15 @@
3838
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3939
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
4040
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
41+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
4142
import org.springframework.security.core.context.SecurityContextHolder;
4243
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4344
import org.springframework.security.core.userdetails.UserDetailsService;
4445
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
4546
import org.springframework.security.crypto.password.PasswordEncoder;
4647
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
48+
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
49+
import org.springframework.util.function.ThrowingSupplier;
4750
import org.springframework.web.accept.ContentNegotiationStrategy;
4851
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
4952
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@@ -161,9 +164,18 @@ private Map<Class<?>, Object> createSharedObjects() {
161164
Map<Class<?>, Object> sharedObjects = new HashMap<>();
162165
sharedObjects.put(ApplicationContext.class, this.context);
163166
sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
167+
sharedObjects.put(PathPatternRequestMatcher.Builder.class, constructRequestMatcherBuilder(this.context));
164168
return sharedObjects;
165169
}
166170

171+
private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
172+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
173+
requestMatcherBuilder.setApplicationContext(context);
174+
requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
175+
requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
176+
return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
177+
}
178+
167179
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
168180

169181
private PasswordEncoder defaultPasswordEncoder;

config/src/main/java/org/springframework/security/config/annotation/web/configurers/AbstractHttpConfigurer.java

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
3939

4040
private SecurityContextHolderStrategy securityContextHolderStrategy;
4141

42-
private PathPatternRequestMatcher.Builder requestMatcherBuilder;
43-
4442
/**
4543
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
4644
* version of the configuration can be applied.
@@ -69,12 +67,7 @@ protected SecurityContextHolderStrategy getSecurityContextHolderStrategy() {
6967
}
7068

7169
protected PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
72-
if (this.requestMatcherBuilder != null) {
73-
return this.requestMatcherBuilder;
74-
}
75-
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
76-
this.requestMatcherBuilder = context.getBean(PathPatternRequestMatcher.Builder.class);
77-
return this.requestMatcherBuilder;
70+
return getBuilder().getSharedObject(PathPatternRequestMatcher.Builder.class);
7871
}
7972

8073
}

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.security.config.annotation.web;
1818

1919
import java.util.List;
20+
import java.util.stream.Stream;
2021

2122
import jakarta.servlet.DispatcherType;
2223
import org.junit.jupiter.api.BeforeEach;
@@ -68,8 +69,8 @@ public void setUp() {
6869
ObjectProvider<ObjectPostProcessor<Object>> given = this.context.getBeanProvider(type);
6970
given(given).willReturn(postProcessors);
7071
given(postProcessors.getObject()).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
71-
given(this.context.getBean(PathPatternRequestMatcher.Builder.class))
72-
.willReturn(PathPatternRequestMatcher.withDefaults());
72+
given(this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class))
73+
.willReturn(new SingleObjectProvider<>(PathPatternRequestMatcher.withDefaults()));
7374
this.matcherRegistry.setApplicationContext(this.context);
7475
}
7576

@@ -165,4 +166,19 @@ static class MockMvcConfiguration {
165166

166167
}
167168

169+
private static final class SingleObjectProvider<T> implements ObjectProvider<T> {
170+
171+
private final T object;
172+
173+
private SingleObjectProvider(T object) {
174+
this.object = object;
175+
}
176+
177+
@Override
178+
public Stream<T> stream() {
179+
return Stream.of(this.object);
180+
}
181+
182+
}
183+
168184
}

config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.springframework.security.config.observation.SecurityObservationSettings;
5555
import org.springframework.security.config.test.SpringTestContext;
5656
import org.springframework.security.config.test.SpringTestContextExtension;
57+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
5758
import org.springframework.security.core.Authentication;
5859
import org.springframework.security.core.authority.AuthorityUtils;
5960
import org.springframework.security.core.authority.SimpleGrantedAuthority;
@@ -1051,12 +1052,19 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
10511052
@EnableWebSecurity
10521053
static class ServletPathConfig {
10531054

1055+
@Bean
1056+
PathPatternRequestMatcherBuilderFactoryBean requesMatcherBuilder() {
1057+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
1058+
bean.setBasePath("/spring");
1059+
return bean;
1060+
}
1061+
10541062
@Bean
10551063
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
10561064
// @formatter:off
10571065
return http
10581066
.authorizeHttpRequests((authorize) -> authorize
1059-
.requestMatchers(builder.basePath("/spring").matcher("/")).hasRole("ADMIN")
1067+
.requestMatchers(builder.matcher("/")).hasRole("ADMIN")
10601068
)
10611069
.build();
10621070
// @formatter:on

config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecurityRequestMatchersTests.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.mock.web.MockServletContext;
3333
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3434
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
35+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
3536
import org.springframework.security.core.userdetails.UserDetailsService;
3637
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
3738
import org.springframework.security.web.FilterChainProxy;
@@ -157,6 +158,11 @@ public void loadConfig(Class<?>... configs) {
157158
@EnableWebMvc
158159
static class MultiMvcMatcherInLambdaConfig {
159160

161+
@Bean
162+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
163+
return new PathPatternRequestMatcherBuilderFactoryBean();
164+
}
165+
160166
@Bean
161167
@Order(Ordered.HIGHEST_PRECEDENCE)
162168
SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
@@ -204,6 +210,11 @@ String path() {
204210
@EnableWebMvc
205211
static class MultiMvcMatcherConfig {
206212

213+
@Bean
214+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
215+
return new PathPatternRequestMatcherBuilderFactoryBean();
216+
}
217+
207218
@Bean
208219
@Order(Ordered.HIGHEST_PRECEDENCE)
209220
SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
@@ -249,6 +260,11 @@ String path() {
249260
@EnableWebMvc
250261
static class MvcMatcherConfig {
251262

263+
@Bean
264+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
265+
return new PathPatternRequestMatcherBuilderFactoryBean();
266+
}
267+
252268
@Bean
253269
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
254270
// @formatter:off
@@ -283,6 +299,11 @@ String path() {
283299
@EnableWebMvc
284300
static class RequestMatchersMvcMatcherConfig {
285301

302+
@Bean
303+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
304+
return new PathPatternRequestMatcherBuilderFactoryBean();
305+
}
306+
286307
@Bean
287308
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
288309
// @formatter:off
@@ -318,6 +339,11 @@ String path() {
318339
@EnableWebMvc
319340
static class RequestMatchersMvcMatcherInLambdaConfig {
320341

342+
@Bean
343+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
344+
return new PathPatternRequestMatcherBuilderFactoryBean();
345+
}
346+
321347
@Bean
322348
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
323349
// @formatter:off
@@ -350,6 +376,11 @@ String path() {
350376
@EnableWebMvc
351377
static class RequestMatchersMvcMatcherServeltPathConfig {
352378

379+
@Bean
380+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
381+
return new PathPatternRequestMatcherBuilderFactoryBean();
382+
}
383+
353384
@Bean
354385
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
355386
// @formatter:off
@@ -386,6 +417,11 @@ String path() {
386417
@EnableWebMvc
387418
static class RequestMatchersMvcMatcherServletPathInLambdaConfig {
388419

420+
@Bean
421+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
422+
return new PathPatternRequestMatcherBuilderFactoryBean();
423+
}
424+
389425
@Bean
390426
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
391427
// @formatter:off

config/src/test/java/org/springframework/security/config/annotation/web/configurers/HttpSecuritySecurityMatchersTests.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.springframework.mock.web.MockHttpServletResponse;
3333
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
3434
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
35+
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
3536
import org.springframework.security.core.userdetails.User;
3637
import org.springframework.security.core.userdetails.UserDetails;
3738
import org.springframework.security.core.userdetails.UserDetailsService;
@@ -354,14 +355,20 @@ String path() {
354355
@Import(UsersConfig.class)
355356
static class SecurityMatchersMvcMatcherServletPathConfig {
356357

358+
@Bean
359+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
360+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
361+
bean.setBasePath("/spring");
362+
return bean;
363+
}
364+
357365
@Bean
358366
SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
359-
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
360367
// @formatter:off
361368
http
362369
.securityMatchers((security) -> security
363-
.requestMatchers(spring.matcher("/path"))
364-
.requestMatchers(spring.matcher("/never-match"))
370+
.requestMatchers(builder.matcher("/path"))
371+
.requestMatchers(builder.matcher("/never-match"))
365372
)
366373
.httpBasic(withDefaults())
367374
.authorizeHttpRequests((authorize) -> authorize
@@ -388,14 +395,20 @@ String path() {
388395
@Import(UsersConfig.class)
389396
static class SecurityMatchersMvcMatcherServletPathInLambdaConfig {
390397

398+
@Bean
399+
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
400+
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
401+
bean.setBasePath("/spring");
402+
return bean;
403+
}
404+
391405
@Bean
392406
SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
393-
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
394407
// @formatter:off
395408
http
396409
.securityMatchers((matchers) -> matchers
397-
.requestMatchers(spring.matcher("/path"))
398-
.requestMatchers(spring.matcher("/never-match"))
410+
.requestMatchers(builder.matcher("/path"))
411+
.requestMatchers(builder.matcher("/never-match"))
399412
)
400413
.httpBasic(withDefaults())
401414
.authorizeHttpRequests((authorize) -> authorize

0 commit comments

Comments
 (0)