diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java index 21f4dc203e..ec33c902f5 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.java @@ -29,14 +29,12 @@ import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.BeanMetadataElement; import org.springframework.beans.BeansException; import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; @@ -113,86 +111,67 @@ class WebMvcSecurityConfiguration implements WebMvcConfigurer, ApplicationContex } } - @Bean - static SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() { - return new SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor(); - } - /** - * Used to ensure Spring MVC request matching is cached. Creates a - * {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named + * Used to ensure Spring MVC request matching is cached. + * + * Creates a {@link BeanDefinitionRegistryPostProcessor} that detects if a bean named * HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME is defined. If so, it moves the * AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME to another bean name * and then adds a {@link CompositeFilter} that contains * {@link HandlerMappingIntrospector#createCacheFilter()} and the original * FilterChainProxy under the original Bean name. - * * @return */ - static class SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor - implements BeanDefinitionRegistryPostProcessor { - - @Override - public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { - if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { - return; + @Bean + static BeanDefinitionRegistryPostProcessor springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor() { + return new BeanDefinitionRegistryPostProcessor() { + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { } - BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder - .rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class) - .addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME) - .getBeanDefinition(); - registry.registerBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer", - hmiRequestTransformer); + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + if (!registry.containsBeanDefinition(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME)) { + return; + } - BeanDefinition filterChainProxy = registry - .getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); + String hmiRequestTransformerBeanName = HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME + "RequestTransformer"; + if (!registry.containsBeanDefinition(hmiRequestTransformerBeanName)) { + BeanDefinition hmiRequestTransformer = BeanDefinitionBuilder + .rootBeanDefinition(HandlerMappingIntrospectorRequestTransformer.class) + .addConstructorArgReference(HANDLER_MAPPING_INTROSPECTOR_BEAN_NAME) + .getBeanDefinition(); + registry.registerBeanDefinition(hmiRequestTransformerBeanName, hmiRequestTransformer); + } - BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder - .rootBeanDefinition(HandlerMappingIntrospectorCachFilterFactoryBean.class) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + BeanDefinition filterChainProxy = registry + .getBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); - ManagedList filters = new ManagedList<>(); - filters.add(hmiCacheFilterBldr.getBeanDefinition()); - filters.add(filterChainProxy); - BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder - .rootBeanDefinition(CompositeFilterChainProxy.class) - .addConstructorArgValue(filters); + if (!filterChainProxy.getResolvableType().isInstance(CompositeFilterChainProxy.class)) { + BeanDefinitionBuilder hmiCacheFilterBldr = BeanDefinitionBuilder + .rootBeanDefinition(HandlerMappingIntrospectorCacheFilterFactoryBean.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); - registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, - compositeSpringSecurityFilterChainBldr.getBeanDefinition()); - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - - } - - } - - /** - * Used to exclude the - * {@link SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor} - * from AOT processing. See gh-14362 - */ - static class SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter - implements BeanRegistrationExcludeFilter { - - @Override - public boolean isExcludedFromAotProcessing(RegisteredBean registeredBean) { - Class beanClass = registeredBean.getBeanClass(); - return SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor.class == beanClass; - } + ManagedList filters = new ManagedList<>(); + filters.add(hmiCacheFilterBldr.getBeanDefinition()); + filters.add(filterChainProxy); + BeanDefinitionBuilder compositeSpringSecurityFilterChainBldr = BeanDefinitionBuilder + .rootBeanDefinition(CompositeFilterChainProxy.class) + .addConstructorArgValue(filters); + registry.removeBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME); + registry.registerBeanDefinition(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME, + compositeSpringSecurityFilterChainBldr.getBeanDefinition()); + } + } + }; } /** * {@link FactoryBean} to defer creation of * {@link HandlerMappingIntrospector#createCacheFilter()} */ - static class HandlerMappingIntrospectorCachFilterFactoryBean + static class HandlerMappingIntrospectorCacheFilterFactoryBean implements ApplicationContextAware, FactoryBean { private ApplicationContext applicationContext; diff --git a/config/src/main/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHints.java b/config/src/main/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHints.java index 216f06d97e..9d292b2477 100644 --- a/config/src/main/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHints.java +++ b/config/src/main/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHints.java @@ -34,7 +34,11 @@ class WebMvcSecurityConfigurationRuntimeHints implements RuntimeHintsRegistrar { hints.reflection() .registerType(TypeReference .of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy"), - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + hints.reflection() + .registerType(TypeReference + .of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$HandlerMappingIntrospectorCacheFilterFactoryBean"), + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); } } diff --git a/config/src/main/resources/META-INF/spring/aot.factories b/config/src/main/resources/META-INF/spring/aot.factories index f8c6762b55..a8b18bef35 100644 --- a/config/src/main/resources/META-INF/spring/aot.factories +++ b/config/src/main/resources/META-INF/spring/aot.factories @@ -4,6 +4,3 @@ org.springframework.security.config.annotation.authentication.configuration.Auth org.springframework.aot.hint.RuntimeHintsRegistrar=\ org.springframework.security.config.aot.hint.OAuth2LoginRuntimeHints,\ org.springframework.security.config.aot.hint.WebMvcSecurityConfigurationRuntimeHints - -org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter=\ -org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration.SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterTests.java deleted file mode 100644 index bcb8b0774e..0000000000 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configuration/SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterTests.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2002-2023 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 java.util.List; - -import org.junit.jupiter.api.Test; - -import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; -import org.springframework.beans.factory.support.RegisteredBean; -import org.springframework.core.io.support.SpringFactoriesLoader; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for - * {@link WebMvcSecurityConfiguration.SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilter} - * - * @author Marcus da Coregio - */ -class SpringSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterTests { - - @Test - void springSecurityHandlerMappingIntrospectorBeanRegistrationExcludeFilterShouldBeExcludedFromAotProcessing() - throws ClassNotFoundException { - List filters = SpringFactoriesLoader - .forResourceLocation("META-INF/spring/aot.factories") - .load(BeanRegistrationExcludeFilter.class); - RegisteredBean registeredBean = mock(RegisteredBean.class); - Class clazz = Class.forName( - "org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$SpringSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor"); - given(registeredBean.getBeanClass()).willReturn((Class) clazz); - boolean isExcluded = filters.stream().anyMatch((f) -> f.isExcludedFromAotProcessing(registeredBean)); - assertThat(isExcluded).isTrue(); - } - -} diff --git a/config/src/test/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHintsTests.java b/config/src/test/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHintsTests.java index 57f9a82750..906deec0e9 100644 --- a/config/src/test/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHintsTests.java +++ b/config/src/test/java/org/springframework/security/config/aot/hint/WebMvcSecurityConfigurationRuntimeHintsTests.java @@ -53,4 +53,12 @@ class WebMvcSecurityConfigurationRuntimeHintsTests { .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); } + @Test + void handlerMappingIntrospectorCacheFilterFactoryBeanHasHints() { + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference + .of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$HandlerMappingIntrospectorCacheFilterFactoryBean")) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); + } + } diff --git a/test/src/main/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHints.java b/test/src/main/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHints.java index d01c72d9b9..2210b36363 100644 --- a/test/src/main/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHints.java +++ b/test/src/main/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHints.java @@ -50,7 +50,7 @@ class WebTestUtilsTestRuntimeHints implements TestRuntimeHintsRegistrar { hints.reflection() .registerType(TypeReference .of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy"), - MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + MemberCategory.INVOKE_DECLARED_METHODS); } private void registerCsrfTokenRepositoryHints(RuntimeHints hints) { diff --git a/test/src/test/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHintsTests.java b/test/src/test/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHintsTests.java index e29fc9308a..53954aab57 100644 --- a/test/src/test/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHintsTests.java +++ b/test/src/test/java/org/springframework/security/test/aot/hint/WebTestUtilsTestRuntimeHintsTests.java @@ -62,8 +62,7 @@ class WebTestUtilsTestRuntimeHintsTests { assertThat(RuntimeHintsPredicates.reflection() .onType(TypeReference .of("org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy")) - .withMemberCategories(MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)) - .accepts(this.hints); + .withMemberCategory(MemberCategory.INVOKE_DECLARED_METHODS)).accepts(this.hints); } @Test