Skip to content

Commit a628384

Browse files
Merge branch '6.2.x'
Closes gh-14368
2 parents 8e66cf9 + 737678c commit a628384

File tree

3 files changed

+120
-39
lines changed

3 files changed

+120
-39
lines changed

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

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,14 @@
2929
import org.springframework.beans.BeanMetadataElement;
3030
import org.springframework.beans.BeansException;
3131
import org.springframework.beans.factory.FactoryBean;
32+
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
3233
import org.springframework.beans.factory.config.BeanDefinition;
3334
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
3435
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3536
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
3637
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
3738
import org.springframework.beans.factory.support.ManagedList;
39+
import org.springframework.beans.factory.support.RegisteredBean;
3840
import org.springframework.context.ApplicationContext;
3941
import org.springframework.context.ApplicationContextAware;
4042
import org.springframework.context.annotation.Bean;
@@ -110,56 +112,79 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
110112
}
111113
}
112114

115+
@Bean
116+
static SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
117+
return new SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor();
118+
}
119+
113120
/**
114-
* Used to ensure Spring MVC request matching is cached.
115-
*
116-
* Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
121+
* Used to ensure Spring MVC request matching is cached. Creates a
122+
* {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named
117123
* HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the
118124
* AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name
119125
* and then adds a {@link CompositeFilter} that contains
120126
* {@link HandlerMappingIntrospector#createCacheFilter()} and the original
121127
* FilterChainProxy under the original Bean name.
128+
*
122129
* @return
123130
*/
124-
@Bean
125-
static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() {
126-
return new BeanDefinitionRegistryPostProcessor() {
127-
@Override
128-
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
131+
static class SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor
132+
implements BeanDefinitionRegistryPostProcessor {
133+
134+
@Override
135+
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
136+
if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
137+
return;
129138
}
130139

131-
@Override
132-
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
133-
if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) {
134-
return;
135-
}
140+
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
141+
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
142+
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
143+
.getBeanDefinition();
144+
registry.registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer",
145+
hmiRequestTransformer);
146+
147+
BeanDefinition filterChainProxy = registry
148+
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
149+
150+
BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder
151+
.rootBeanDefinition(HandlerMappingIntrospectorCachFilterFactoryBean.class)
152+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
153+
154+
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
155+
filters.add(hmiCacheFilterBldr.getBeanDefinition());
156+
filters.add(filterChainProxy);
157+
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
158+
.rootBeanDefinition(CompositeFilterChainProxy.class)
159+
.addConstructorArgValue(filters);
160+
161+
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
162+
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
163+
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
164+
}
165+
166+
@Override
167+
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
168+
169+
}
170+
171+
}
172+
173+
/**
174+
* Used to exclude the
175+
* {@link SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor}
176+
* from AOT processing. See <a href=
177+
* "https://github.com/spring-projects/spring-security/issues/14362">gh-14362</a>
178+
*/
179+
static class SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter
180+
implements BeanRegistrationExcludeFilter {
181+
182+
@Override
183+
public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) {
184+
Class<?> beanClass = registeredBean.getBeanClass();
185+
return SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor.class == beanClass;
186+
}
136187

137-
BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder
138-
.rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class)
139-
.addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)
140-
.getBeanDefinition();
141-
registry.registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer",
142-
hmiRequestTransformer);
143-
144-
BeanDefinition filterChainProxy = registry
145-
.getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
146-
147-
BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder
148-
.rootBeanDefinition(HandlerMappingIntrospectorCachFilterFactoryBean.class)
149-
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
150-
151-
ManagedList<BeanMetadataElement> filters = new ManagedList<>();
152-
filters.add(hmiCacheFilterBldr.getBeanDefinition());
153-
filters.add(filterChainProxy);
154-
BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder
155-
.rootBeanDefinition(CompositeFilterChainProxy.class)
156-
.addConstructorArgValue(filters);
157-
158-
registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME);
159-
registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME,
160-
compositeSpringSecurityFilterChainBldr.getBeanDefinition());
161-
}
162-
};
163188
}
164189

165190
/**

config/src/main/resources/META-INF/spring/aot.factories

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ org.springframework.security.config.annotation.authentication.configuration.Auth
33

44
org.springframework.aot.hint.RuntimeHintsRegistrar=\
55
org.springframework.security.config.aot.hint.OAuth2LoginRuntimeHints
6+
7+
org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter=\
8+
org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration.SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2002-2023 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.configuration;
18+
19+
import java.util.List;
20+
21+
import org.junit.jupiter.api.Test;
22+
23+
import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter;
24+
import org.springframework.beans.factory.support.RegisteredBean;
25+
import org.springframework.core.io.support.SpringFactoriesLoader;
26+
27+
import static org.assertj.core.api.Assertions.assertThat;
28+
import static org.mockito.BDDMockito.given;
29+
import static org.mockito.Mockito.mock;
30+
31+
/**
32+
* Tests for
33+
* {@link WebMvcSecurityConfiguration.SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter}
34+
*
35+
* @author Marcus da Coregio
36+
*/
37+
class SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterTests {
38+
39+
@Test
40+
void springSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterShouldBeExcludedFromAotProcessing()
41+
throws ClassNotFoundException {
42+
List<BeanRegistrationExcludeFilter> filters = SpringFactoriesLoader
43+
.forResourceLocation("META-INF/spring/aot.factories")
44+
.load(BeanRegistrationExcludeFilter.class);
45+
RegisteredBean registeredBean = mock(RegisteredBean.class);
46+
Class<?> clazz = Class.forName(
47+
"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor");
48+
given(registeredBean.getBeanClass()).willReturn((Class) clazz);
49+
boolean isExcluded = filters.stream().anyMatch((f) -> f.isExcludedFromAotProcessing(registeredBean));
50+
assertThat(isExcluded).isTrue();
51+
}
52+
53+
}

0 commit comments

Comments
 (0)