mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-10-24 11:18:45 +00:00
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
This commit is contained in:
parent
006f638c0a
commit
aeb2dbc2b6
@ -27,11 +27,13 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.DispatcherTypeRequestMatcher;
|
||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
|
||||
/**
|
||||
* A base class for registering {@link RequestMatcher}'s. For example, it might allow for
|
||||
@ -52,6 +54,8 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
|
||||
private final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private PathPatternRequestMatcher.Builder requestMatcherBuilder;
|
||||
|
||||
protected final void setApplicationContext(ApplicationContext context) {
|
||||
this.context = context;
|
||||
}
|
||||
@ -140,7 +144,7 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
+ "Spring Security, leaving out the leading slash will result in an exception.");
|
||||
}
|
||||
Assert.state(!this.anyRequestConfigured, "Can't configure requestMatchers after anyRequest");
|
||||
PathPatternRequestMatcher.Builder builder = this.context.getBean(PathPatternRequestMatcher.Builder.class);
|
||||
PathPatternRequestMatcher.Builder builder = getRequestMatcherBuilder();
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
for (String pattern : patterns) {
|
||||
matchers.add(builder.matcher(method, pattern));
|
||||
@ -148,6 +152,23 @@ public abstract class AbstractRequestMatcherRegistry<C> {
|
||||
return requestMatchers(matchers.toArray(new RequestMatcher[0]));
|
||||
}
|
||||
|
||||
private PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
|
||||
if (this.requestMatcherBuilder != null) {
|
||||
return this.requestMatcherBuilder;
|
||||
}
|
||||
this.requestMatcherBuilder = this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class)
|
||||
.getIfUnique(() -> constructRequestMatcherBuilder(this.context));
|
||||
return this.requestMatcherBuilder;
|
||||
}
|
||||
|
||||
private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
requestMatcherBuilder.setApplicationContext(context);
|
||||
requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
|
||||
requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
|
||||
return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
|
||||
}
|
||||
|
||||
private boolean anyPathsDontStartWithLeadingSlash(String... patterns) {
|
||||
for (String pattern : patterns) {
|
||||
if (!pattern.startsWith("/")) {
|
||||
|
||||
@ -2058,7 +2058,7 @@ public final class HttpSecurity extends AbstractConfiguredSecurityBuilder<Defaul
|
||||
*/
|
||||
public HttpSecurity securityMatcher(String... patterns) {
|
||||
List<RequestMatcher> matchers = new ArrayList<>();
|
||||
PathPatternRequestMatcher.Builder builder = getContext().getBean(PathPatternRequestMatcher.Builder.class);
|
||||
PathPatternRequestMatcher.Builder builder = getSharedObject(PathPatternRequestMatcher.Builder.class);
|
||||
for (String pattern : patterns) {
|
||||
matchers.add(builder.matcher(pattern));
|
||||
}
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.config.annotation.web.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Fallback;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
|
||||
class AuthorizationConfiguration {
|
||||
|
||||
@Bean
|
||||
@Fallback
|
||||
PathPatternRequestMatcherBuilderFactoryBean pathPatternRequestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
}
|
||||
@ -83,7 +83,7 @@ import org.springframework.security.web.SecurityFilterChain;
|
||||
@Target(ElementType.TYPE)
|
||||
@Documented
|
||||
@Import({ WebSecurityConfiguration.class, SpringWebMvcImportSelector.class, OAuth2ImportSelector.class,
|
||||
HttpSecurityConfiguration.class, ObservationImportSelector.class, AuthorizationConfiguration.class })
|
||||
HttpSecurityConfiguration.class, ObservationImportSelector.class })
|
||||
@EnableGlobalAuthentication
|
||||
public @interface EnableWebSecurity {
|
||||
|
||||
|
||||
@ -38,12 +38,15 @@ import org.springframework.security.config.annotation.authentication.configurers
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.DefaultLoginPageConfigurer;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.context.SecurityContextHolderStrategy;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
|
||||
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
|
||||
import org.springframework.util.function.ThrowingSupplier;
|
||||
import org.springframework.web.accept.ContentNegotiationStrategy;
|
||||
import org.springframework.web.accept.HeaderContentNegotiationStrategy;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
@ -161,9 +164,18 @@ class HttpSecurityConfiguration {
|
||||
Map<Class<?>, Object> sharedObjects = new HashMap<>();
|
||||
sharedObjects.put(ApplicationContext.class, this.context);
|
||||
sharedObjects.put(ContentNegotiationStrategy.class, this.contentNegotiationStrategy);
|
||||
sharedObjects.put(PathPatternRequestMatcher.Builder.class, constructRequestMatcherBuilder(this.context));
|
||||
return sharedObjects;
|
||||
}
|
||||
|
||||
private PathPatternRequestMatcher.Builder constructRequestMatcherBuilder(ApplicationContext context) {
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder = new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
requestMatcherBuilder.setApplicationContext(context);
|
||||
requestMatcherBuilder.setBeanFactory(context.getAutowireCapableBeanFactory());
|
||||
requestMatcherBuilder.setBeanName(requestMatcherBuilder.toString());
|
||||
return ThrowingSupplier.of(requestMatcherBuilder::getObject).get();
|
||||
}
|
||||
|
||||
static class DefaultPasswordEncoderAuthenticationManagerBuilder extends AuthenticationManagerBuilder {
|
||||
|
||||
private PasswordEncoder defaultPasswordEncoder;
|
||||
|
||||
@ -39,8 +39,6 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
|
||||
|
||||
private SecurityContextHolderStrategy securityContextHolderStrategy;
|
||||
|
||||
private PathPatternRequestMatcher.Builder requestMatcherBuilder;
|
||||
|
||||
/**
|
||||
* Disables the {@link AbstractHttpConfigurer} by removing it. After doing so a fresh
|
||||
* version of the configuration can be applied.
|
||||
@ -69,12 +67,7 @@ public abstract class AbstractHttpConfigurer<T extends AbstractHttpConfigurer<T,
|
||||
}
|
||||
|
||||
protected PathPatternRequestMatcher.Builder getRequestMatcherBuilder() {
|
||||
if (this.requestMatcherBuilder != null) {
|
||||
return this.requestMatcherBuilder;
|
||||
}
|
||||
ApplicationContext context = getBuilder().getSharedObject(ApplicationContext.class);
|
||||
this.requestMatcherBuilder = context.getBean(PathPatternRequestMatcher.Builder.class);
|
||||
return this.requestMatcherBuilder;
|
||||
return getBuilder().getSharedObject(PathPatternRequestMatcher.Builder.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
package org.springframework.security.config.annotation.web;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jakarta.servlet.DispatcherType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
@ -68,8 +69,8 @@ public class AbstractRequestMatcherRegistryTests {
|
||||
ObjectProvider<ObjectPostProcessor<Object>> given = this.context.getBeanProvider(type);
|
||||
given(given).willReturn(postProcessors);
|
||||
given(postProcessors.getObject()).willReturn(NO_OP_OBJECT_POST_PROCESSOR);
|
||||
given(this.context.getBean(PathPatternRequestMatcher.Builder.class))
|
||||
.willReturn(PathPatternRequestMatcher.withDefaults());
|
||||
given(this.context.getBeanProvider(PathPatternRequestMatcher.Builder.class))
|
||||
.willReturn(new SingleObjectProvider<>(PathPatternRequestMatcher.withDefaults()));
|
||||
this.matcherRegistry.setApplicationContext(this.context);
|
||||
}
|
||||
|
||||
@ -165,4 +166,19 @@ public class AbstractRequestMatcherRegistryTests {
|
||||
|
||||
}
|
||||
|
||||
private static final class SingleObjectProvider<T> implements ObjectProvider<T> {
|
||||
|
||||
private final T object;
|
||||
|
||||
private SingleObjectProvider(T object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> stream() {
|
||||
return Stream.of(this.object);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -54,6 +54,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.config.observation.SecurityObservationSettings;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
@ -1051,12 +1052,19 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||
@EnableWebSecurity
|
||||
static class ServletPathConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requesMatcherBuilder() {
|
||||
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
bean.setBasePath("/spring");
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
return http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.requestMatchers(builder.basePath("/spring").matcher("/")).hasRole("ADMIN")
|
||||
.requestMatchers(builder.matcher("/")).hasRole("ADMIN")
|
||||
)
|
||||
.build();
|
||||
// @formatter:on
|
||||
|
||||
@ -32,6 +32,7 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
import org.springframework.security.web.FilterChainProxy;
|
||||
@ -157,6 +158,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class MultiMvcMatcherInLambdaConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
@ -204,6 +210,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class MultiMvcMatcherConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
SecurityFilterChain first(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
@ -249,6 +260,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class MvcMatcherConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
@ -283,6 +299,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class RequestMatchersMvcMatcherConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
@ -318,6 +339,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class RequestMatchersMvcMatcherInLambdaConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
@ -350,6 +376,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class RequestMatchersMvcMatcherServeltPathConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
@ -386,6 +417,11 @@ public class HttpSecurityRequestMatchersTests {
|
||||
@EnableWebMvc
|
||||
static class RequestMatchersMvcMatcherServletPathInLambdaConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
return new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
// @formatter:off
|
||||
|
||||
@ -32,6 +32,7 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.web.PathPatternRequestMatcherBuilderFactoryBean;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
@ -354,14 +355,20 @@ public class HttpSecuritySecurityMatchersTests {
|
||||
@Import(UsersConfig.class)
|
||||
static class SecurityMatchersMvcMatcherServletPathConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
bean.setBasePath("/spring");
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
|
||||
// @formatter:off
|
||||
http
|
||||
.securityMatchers((security) -> security
|
||||
.requestMatchers(spring.matcher("/path"))
|
||||
.requestMatchers(spring.matcher("/never-match"))
|
||||
.requestMatchers(builder.matcher("/path"))
|
||||
.requestMatchers(builder.matcher("/never-match"))
|
||||
)
|
||||
.httpBasic(withDefaults())
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
@ -388,14 +395,20 @@ public class HttpSecuritySecurityMatchersTests {
|
||||
@Import(UsersConfig.class)
|
||||
static class SecurityMatchersMvcMatcherServletPathInLambdaConfig {
|
||||
|
||||
@Bean
|
||||
PathPatternRequestMatcherBuilderFactoryBean requestMatcherBuilder() {
|
||||
PathPatternRequestMatcherBuilderFactoryBean bean = new PathPatternRequestMatcherBuilderFactoryBean();
|
||||
bean.setBasePath("/spring");
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Bean
|
||||
SecurityFilterChain appSecurity(HttpSecurity http, PathPatternRequestMatcher.Builder builder) throws Exception {
|
||||
PathPatternRequestMatcher.Builder spring = builder.basePath("/spring");
|
||||
// @formatter:off
|
||||
http
|
||||
.securityMatchers((matchers) -> matchers
|
||||
.requestMatchers(spring.matcher("/path"))
|
||||
.requestMatchers(spring.matcher("/never-match"))
|
||||
.requestMatchers(builder.matcher("/path"))
|
||||
.requestMatchers(builder.matcher("/never-match"))
|
||||
)
|
||||
.httpBasic(withDefaults())
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user