diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.java index a5f8242894..ef8c9a1cd3 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.java @@ -16,12 +16,24 @@ package org.springframework.security.config.annotation.method.configuration; +import org.aopalliance.aop.Advice; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import org.springframework.aop.Advisor; +import org.springframework.aop.Pointcut; +import org.springframework.aop.PointcutAdvisor; +import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.Ordered; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.security.authorization.method.AuthorizationAdvisor; class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar { @@ -49,9 +61,49 @@ class MethodSecurityAdvisorRegistrar implements ImportBeanDefinitionRegistrar { if (!(definition instanceof RootBeanDefinition)) { return; } - RootBeanDefinition advisor = new RootBeanDefinition((RootBeanDefinition) definition); + BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AdvisorWrapper.class); + builder.setFactoryMethod("of"); + builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + builder.addConstructorArgReference(interceptorName); + RootBeanDefinition advisor = (RootBeanDefinition) builder.getBeanDefinition(); advisor.setTargetType(Advisor.class); - registry.registerBeanDefinition(prefix + "Advisor", advisor); + registry.registerBeanDefinition(advisorName, advisor); + } + + public static final class AdvisorWrapper + implements PointcutAdvisor, MethodInterceptor, Ordered, AopInfrastructureBean { + + private final AuthorizationAdvisor advisor; + + private AdvisorWrapper(AuthorizationAdvisor advisor) { + this.advisor = advisor; + } + + public static AdvisorWrapper of(AuthorizationAdvisor advisor) { + return new AdvisorWrapper(advisor); + } + + @Override + public Advice getAdvice() { + return this.advisor.getAdvice(); + } + + @Override + public Pointcut getPointcut() { + return this.advisor.getPointcut(); + } + + @Override + public int getOrder() { + return this.advisor.getOrder(); + } + + @Nullable + @Override + public Object invoke(@NotNull MethodInvocation invocation) throws Throwable { + return this.advisor.invoke(invocation); + } + } } diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java index a2f82afe6d..346e08c5c2 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java @@ -35,10 +35,13 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.aop.Advisor; +import org.springframework.aop.config.AopConfigUtils; import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.JdkRegexpMethodPointcut; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -63,6 +66,7 @@ import org.springframework.security.access.prepost.PreFilter; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.method.AuthorizationAdvisor; import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory; import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor; import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder; @@ -82,6 +86,7 @@ import org.springframework.security.core.context.SecurityContextHolderStrategy; import org.springframework.security.test.context.support.WithAnonymousUser; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.security.test.context.support.WithSecurityContextTestExecutionListener; +import org.springframework.stereotype.Component; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestExecutionListeners; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -953,6 +958,32 @@ public class PrePostMethodSecurityConfigurationTests { this.spring.getContext().getBean(ClassInheritingAbstractClassWithNoAnnotations.class).method(); } + // gh-15592 + @Test + void autowireWhenDefaultsThenCreatesExactlyOneAdvisorPerAnnotation() { + this.spring.register(MethodSecurityServiceConfig.class).autowire(); + AuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext() + .getBean(AuthorizationAdvisorProxyFactory.class); + assertThat(proxyFactory).hasSize(5); + assertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5) + .containsExactlyInAnyOrder("preFilterAuthorizationMethodInterceptor", + "preAuthorizeAuthorizationMethodInterceptor", "postAuthorizeAuthorizationMethodInterceptor", + "postFilterAuthorizationMethodInterceptor", "authorizeReturnObjectMethodInterceptor"); + } + + // gh-15592 + @Test + void autowireWhenAspectJAutoProxyAndFactoryBeanThenExactlyOneAdvisorPerAnnotation() { + this.spring.register(AspectJAwareAutoProxyAndFactoryBeansConfig.class).autowire(); + AuthorizationAdvisorProxyFactory proxyFactory = this.spring.getContext() + .getBean(AuthorizationAdvisorProxyFactory.class); + assertThat(proxyFactory).hasSize(5); + assertThat(this.spring.getContext().getBeanNamesForType(AuthorizationAdvisor.class)).hasSize(5) + .containsExactlyInAnyOrder("preFilterAuthorizationMethodInterceptor", + "preAuthorizeAuthorizationMethodInterceptor", "postAuthorizeAuthorizationMethodInterceptor", + "postFilterAuthorizationMethodInterceptor", "authorizeReturnObjectMethodInterceptor"); + } + private static Consumer disallowBeanOverriding() { return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false); } @@ -1514,4 +1545,30 @@ public class PrePostMethodSecurityConfigurationTests { } + @Configuration + @EnableMethodSecurity + static class AspectJAwareAutoProxyAndFactoryBeansConfig { + + @Bean + static BeanDefinitionRegistryPostProcessor beanDefinitionRegistryPostProcessor() { + return AopConfigUtils::registerAspectJAnnotationAutoProxyCreatorIfNecessary; + } + + @Component + static class MyFactoryBean implements FactoryBean { + + @Override + public Object getObject() throws Exception { + return new Object(); + } + + @Override + public Class getObjectType() { + return Object.class; + } + + } + + } + }