diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/DeferringMethodInterceptor.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/DeferringMethodInterceptor.java new file mode 100644 index 00000000000..3f9f08ccaa6 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/DeferringMethodInterceptor.java @@ -0,0 +1,51 @@ +package org.springframework.security.config.annotation.method.configuration; + +import java.util.function.Supplier; + +import org.aopalliance.aop.Advice; +import org.aopalliance.intercept.MethodInvocation; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import org.springframework.aop.Pointcut; +import org.springframework.security.authorization.method.AuthorizationAdvisor; +import org.springframework.util.function.SingletonSupplier; + +final class DeferringMethodInterceptor implements AuthorizationAdvisor { + + private final Pointcut pointcut; + + private final Supplier delegate; + + DeferringMethodInterceptor(Pointcut pointcut, Supplier delegate) { + this.pointcut = pointcut; + this.delegate = SingletonSupplier.of(delegate); + } + + @Nullable + @Override + public Object invoke(@NotNull MethodInvocation invocation) throws Throwable { + return this.delegate.get().invoke(invocation); + } + + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + @Override + public Advice getAdvice() { + return this; + } + + @Override + public int getOrder() { + return this.delegate.get().getOrder(); + } + + @Override + public boolean isPerInstance() { + return true; + } + +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java index 45908fb549a..e788ecc4a4d 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/Jsr250MethodSecurityConfiguration.java @@ -16,27 +16,30 @@ package org.springframework.security.config.annotation.method.configuration; +import java.util.function.Supplier; + import io.micrometer.observation.ObservationRegistry; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.Pointcut; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportAware; import org.springframework.context.annotation.Role; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.ObservationAuthorizationManager; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.Jsr250AuthorizationManager; import org.springframework.security.config.core.GrantedAuthorityDefaults; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; /** @@ -47,42 +50,64 @@ * @since 5.6 * @see EnableMethodSecurity */ -@Configuration(proxyBeanMethods = false) +@Configuration(value = "_jsr250MethodSecurityConfiguration", proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) final class Jsr250MethodSecurityConfiguration implements ImportAware, AopInfrastructureBean { - private int interceptorOrderOffset; + private static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.jsr250().getPointcut(); + + private final Jsr250AuthorizationManager authorizationManager = new Jsr250AuthorizationManager(); + + private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor + .jsr250(this.authorizationManager); @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor jsr250AuthorizationMethodInterceptor( - ObjectProvider defaultsProvider, - ObjectProvider strategyProvider, - ObjectProvider eventPublisherProvider, - ObjectProvider registryProvider, ObjectProvider roleHierarchyProvider, - Jsr250MethodSecurityConfiguration configuration) { - Jsr250AuthorizationManager jsr250 = new Jsr250AuthorizationManager(); - AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); - RoleHierarchy roleHierarchy = roleHierarchyProvider.getIfAvailable(NullRoleHierarchy::new); - authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy); - jsr250.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); - defaultsProvider.ifAvailable((d) -> jsr250.setRolePrefix(d.getRolePrefix())); - SecurityContextHolderStrategy strategy = strategyProvider - .getIfAvailable(SecurityContextHolder::getContextHolderStrategy); - AuthorizationManager manager = new DeferringObservationAuthorizationManager<>( - registryProvider, jsr250); - AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor - .jsr250(manager); - interceptor.setOrder(interceptor.getOrder() + configuration.interceptorOrderOffset); - interceptor.setSecurityContextHolderStrategy(strategy); - eventPublisherProvider.ifAvailable(interceptor::setAuthorizationEventPublisher); - return interceptor; + ObjectProvider _jsr250MethodSecurityConfiguration) { + Supplier supplier = () -> { + Jsr250MethodSecurityConfiguration configuration = _jsr250MethodSecurityConfiguration.getObject(); + return configuration.methodInterceptor; + }; + return new DeferringMethodInterceptor<>(pointcut, supplier); } @Override public void setImportMetadata(AnnotationMetadata importMetadata) { EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize(); - this.interceptorOrderOffset = annotation.offset(); + this.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset()); + } + + @Autowired(required = false) + void setGrantedAuthorityDefaults(GrantedAuthorityDefaults defaults) { + this.authorizationManager.setRolePrefix(defaults.getRolePrefix()); + } + + @Autowired(required = false) + void setRoleHierarchy(RoleHierarchy roleHierarchy) { + AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); + authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy); + this.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); + } + + @Autowired(required = false) + public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + } + + @Autowired(required = false) + void setObservationRegistry(ObservationRegistry registry) { + if (registry.isNoop()) { + return; + } + AuthorizationManager observed = new ObservationAuthorizationManager<>(registry, + this.authorizationManager); + this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed); + } + + @Autowired(required = false) + void setEventPublisher(AuthorizationEventPublisher eventPublisher) { + this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher); } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java index b9a917033bd..9281e0ebf84 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.java @@ -16,21 +16,14 @@ package org.springframework.security.config.annotation.method.configuration; -import java.util.List; -import java.util.function.Consumer; -import java.util.function.Supplier; - import io.micrometer.observation.ObservationRegistry; -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.Pointcut; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.beans.BeansException; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -41,11 +34,9 @@ import org.springframework.core.type.AnnotationMetadata; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; -import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.authorization.AuthorizationEventPublisher; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.authorization.method.AuthorizationAdvisor; +import org.springframework.security.authorization.ObservationAuthorizationManager; import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager; @@ -55,7 +46,6 @@ import org.springframework.security.authorization.method.PrePostTemplateDefaults; import org.springframework.security.config.core.GrantedAuthorityDefaults; import org.springframework.security.core.context.SecurityContextHolderStrategy; -import org.springframework.util.function.SingletonSupplier; /** * Base {@link Configuration} for enabling Spring Security Method Security. @@ -67,166 +57,140 @@ */ @Configuration(value = "_prePostMethodSecurityConfiguration", proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) -final class PrePostMethodSecurityConfiguration implements ImportAware, AopInfrastructureBean { +final class PrePostMethodSecurityConfiguration implements ImportAware, ApplicationContextAware, AopInfrastructureBean { + + private static final Pointcut preFilterPointcut = new PreFilterAuthorizationMethodInterceptor().getPointcut(); + + private static final Pointcut preAuthorizePointcut = AuthorizationManagerBeforeMethodInterceptor.preAuthorize() + .getPointcut(); + + private static final Pointcut postAuthorizePointcut = AuthorizationManagerAfterMethodInterceptor.postAuthorize() + .getPointcut(); + + private static final Pointcut postFilterPointcut = new PostFilterAuthorizationMethodInterceptor().getPointcut(); + + private final PreAuthorizeAuthorizationManager preAuthorizeAuthorizationManager = new PreAuthorizeAuthorizationManager(); + + private final PostAuthorizeAuthorizationManager postAuthorizeAuthorizationManager = new PostAuthorizeAuthorizationManager(); + + private final PreFilterAuthorizationMethodInterceptor preFilterMethodInterceptor = new PreFilterAuthorizationMethodInterceptor(); - private int interceptorOrderOffset; + private AuthorizationManagerBeforeMethodInterceptor preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor + .preAuthorize(this.preAuthorizeAuthorizationManager); + + private AuthorizationManagerAfterMethodInterceptor postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor + .postAuthorize(this.postAuthorizeAuthorizationManager); + + private final PostFilterAuthorizationMethodInterceptor postFilterMethodInterceptor = new PostFilterAuthorizationMethodInterceptor(); + + private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler(); + + { + this.preFilterMethodInterceptor.setExpressionHandler(this.expressionHandler); + this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler); + this.postAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler); + this.postFilterMethodInterceptor.setExpressionHandler(this.expressionHandler); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.expressionHandler.setApplicationContext(context); + this.preAuthorizeAuthorizationManager.setApplicationContext(context); + this.postAuthorizeAuthorizationManager.setApplicationContext(context); + } + + @Autowired(required = false) + void setGrantedAuthorityDefaults(GrantedAuthorityDefaults grantedAuthorityDefaults) { + this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix()); + } + + @Autowired(required = false) + void setRoleHierarchy(RoleHierarchy roleHierarchy) { + this.expressionHandler.setRoleHierarchy(roleHierarchy); + } + + @Autowired(required = false) + void setTemplateDefaults(PrePostTemplateDefaults templateDefaults) { + this.preFilterMethodInterceptor.setTemplateDefaults(templateDefaults); + this.preAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults); + this.postAuthorizeAuthorizationManager.setTemplateDefaults(templateDefaults); + this.postFilterMethodInterceptor.setTemplateDefaults(templateDefaults); + } + + @Autowired(required = false) + void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) { + this.preFilterMethodInterceptor.setExpressionHandler(expressionHandler); + this.preAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler); + this.postAuthorizeAuthorizationManager.setExpressionHandler(expressionHandler); + this.postFilterMethodInterceptor.setExpressionHandler(expressionHandler); + } + + @Autowired(required = false) + void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.preFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + this.preAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + this.postAuthorizeMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + this.postFilterMethodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + } + + @Autowired(required = false) + void setObservationRegistry(ObservationRegistry registry) { + if (registry.isNoop()) { + return; + } + this.preAuthorizeMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor + .preAuthorize(new ObservationAuthorizationManager<>(registry, this.preAuthorizeAuthorizationManager)); + this.postAuthorizeMethodInterceptor = AuthorizationManagerAfterMethodInterceptor + .postAuthorize(new ObservationAuthorizationManager<>(registry, this.postAuthorizeAuthorizationManager)); + } + + @Autowired(required = false) + void setAuthorizationEventPublisher(AuthorizationEventPublisher publisher) { + this.preAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher); + this.postAuthorizeMethodInterceptor.setAuthorizationEventPublisher(publisher); + } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor preFilterAuthorizationMethodInterceptor( - ObjectProvider defaultsProvider, - ObjectProvider methodSecurityDefaultsProvider, - ObjectProvider expressionHandlerProvider, - ObjectProvider strategyProvider, - ObjectProvider roleHierarchyProvider) { - PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor(); - return new DeferringMethodInterceptor<>(preFilter, (f) -> { - methodSecurityDefaultsProvider.ifAvailable(f::setTemplateDefaults); - strategyProvider.ifAvailable(f::setSecurityContextHolderStrategy); - }, List.of((context) -> preFilter.setExpressionHandler(expressionHandlerProvider - .getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context))))); + ObjectProvider _prePostMethodSecurityConfiguration) { + return new DeferringMethodInterceptor<>(preFilterPointcut, + () -> _prePostMethodSecurityConfiguration.getObject().preFilterMethodInterceptor); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor( - ObjectProvider defaultsProvider, - ObjectProvider methodSecurityDefaultsProvider, - ObjectProvider expressionHandlerProvider, - ObjectProvider strategyProvider, - ObjectProvider eventPublisherProvider, - ObjectProvider registryProvider, ObjectProvider roleHierarchyProvider) { - PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); - AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor - .preAuthorize(manager(manager, registryProvider)); - return new DeferringMethodInterceptor<>(preAuthorize, (f) -> { - methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults); - strategyProvider.ifAvailable(f::setSecurityContextHolderStrategy); - eventPublisherProvider.ifAvailable(f::setAuthorizationEventPublisher); - }, List.of(manager::setApplicationContext, (context) -> manager.setExpressionHandler(expressionHandlerProvider - .getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context))))); + ObjectProvider _prePostMethodSecurityConfiguration) { + return new DeferringMethodInterceptor<>(preAuthorizePointcut, + () -> _prePostMethodSecurityConfiguration.getObject().preAuthorizeMethodInterceptor); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor( - ObjectProvider defaultsProvider, - ObjectProvider methodSecurityDefaultsProvider, - ObjectProvider expressionHandlerProvider, - ObjectProvider strategyProvider, - ObjectProvider eventPublisherProvider, - ObjectProvider registryProvider, ObjectProvider roleHierarchyProvider) { - PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); - AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor - .postAuthorize(manager(manager, registryProvider)); - return new DeferringMethodInterceptor<>(postAuthorize, (f) -> { - methodSecurityDefaultsProvider.ifAvailable(manager::setTemplateDefaults); - strategyProvider.ifAvailable(f::setSecurityContextHolderStrategy); - eventPublisherProvider.ifAvailable(f::setAuthorizationEventPublisher); - }, List.of(manager::setApplicationContext, (context) -> manager.setExpressionHandler(expressionHandlerProvider - .getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context))))); + ObjectProvider _prePostMethodSecurityConfiguration) { + return new DeferringMethodInterceptor<>(postAuthorizePointcut, + () -> _prePostMethodSecurityConfiguration.getObject().postAuthorizeMethodInterceptor); } @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor postFilterAuthorizationMethodInterceptor( - ObjectProvider defaultsProvider, - ObjectProvider methodSecurityDefaultsProvider, - ObjectProvider expressionHandlerProvider, - ObjectProvider strategyProvider, - ObjectProvider roleHierarchyProvider) { - PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor(); - return new DeferringMethodInterceptor<>(postFilter, (f) -> { - methodSecurityDefaultsProvider.ifAvailable(f::setTemplateDefaults); - strategyProvider.ifAvailable(f::setSecurityContextHolderStrategy); - }, List.of((context) -> postFilter.setExpressionHandler(expressionHandlerProvider - .getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, roleHierarchyProvider, context))))); - } - - private static MethodSecurityExpressionHandler defaultExpressionHandler( - ObjectProvider defaultsProvider, - ObjectProvider roleHierarchyProvider, ApplicationContext context) { - DefaultMethodSecurityExpressionHandler handler = new DefaultMethodSecurityExpressionHandler(); - RoleHierarchy roleHierarchy = roleHierarchyProvider.getIfAvailable(NullRoleHierarchy::new); - handler.setRoleHierarchy(roleHierarchy); - defaultsProvider.ifAvailable((d) -> handler.setDefaultRolePrefix(d.getRolePrefix())); - handler.setApplicationContext(context); - return handler; - } - - static AuthorizationManager manager(AuthorizationManager delegate, - ObjectProvider registryProvider) { - return new DeferringObservationAuthorizationManager<>(registryProvider, delegate); + ObjectProvider _prePostMethodSecurityConfiguration) { + return new DeferringMethodInterceptor<>(postFilterPointcut, + () -> _prePostMethodSecurityConfiguration.getObject().postFilterMethodInterceptor); } @Override public void setImportMetadata(AnnotationMetadata importMetadata) { EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize(); - this.interceptorOrderOffset = annotation.offset(); - } - - private static final class DeferringMethodInterceptor - implements AuthorizationAdvisor, ApplicationContextAware { - - private final Pointcut pointcut; - - private int order; - - private boolean contextProvided = false; - - private final List> needsApplicationContext; - - private final Supplier delegate; - - DeferringMethodInterceptor(M delegate, Consumer supplier, - List> needsApplicationContext) { - this.pointcut = delegate.getPointcut(); - this.order = delegate.getOrder(); - this.delegate = SingletonSupplier.of(() -> { - supplier.accept(delegate); - return delegate; - }); - this.needsApplicationContext = needsApplicationContext; - } - - @Nullable - @Override - public Object invoke(@NotNull MethodInvocation invocation) throws Throwable { - return this.delegate.get().invoke(invocation); - } - - @Override - public Pointcut getPointcut() { - return this.pointcut; - } - - @Override - public Advice getAdvice() { - return this; - } - - @Override - public int getOrder() { - return this.order; - } - - @Override - public boolean isPerInstance() { - return true; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - if (!this.contextProvided) { - this.contextProvided = true; - this.order += applicationContext.getBean("_prePostMethodSecurityConfiguration", - PrePostMethodSecurityConfiguration.class).interceptorOrderOffset; - for (Consumer needs : this.needsApplicationContext) { - needs.accept(applicationContext); - } - } - } - + this.preFilterMethodInterceptor.setOrder(this.preFilterMethodInterceptor.getOrder() + annotation.offset()); + this.preAuthorizeMethodInterceptor + .setOrder(this.preAuthorizeMethodInterceptor.getOrder() + annotation.offset()); + this.postAuthorizeMethodInterceptor + .setOrder(this.postAuthorizeMethodInterceptor.getOrder() + annotation.offset()); + this.postFilterMethodInterceptor.setOrder(this.postFilterMethodInterceptor.getOrder() + annotation.offset()); } } diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java index 2b6a2e29280..08375d7c3b0 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/SecuredMethodSecurityConfiguration.java @@ -16,12 +16,16 @@ package org.springframework.security.config.annotation.method.configuration; +import java.util.function.Supplier; + import io.micrometer.observation.ObservationRegistry; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; +import org.springframework.aop.Pointcut; import org.springframework.aop.framework.AopInfrastructureBean; import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -29,14 +33,13 @@ import org.springframework.context.annotation.Role; import org.springframework.core.type.AnnotationMetadata; import org.springframework.security.access.annotation.Secured; -import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.ObservationAuthorizationManager; import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor; import org.springframework.security.authorization.method.SecuredAuthorizationManager; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; /** @@ -47,40 +50,59 @@ * @since 5.6 * @see EnableMethodSecurity */ -@Configuration(proxyBeanMethods = false) +@Configuration(value = "_securedMethodSecurityConfiguration", proxyBeanMethods = false) @Role(BeanDefinition.ROLE_INFRASTRUCTURE) final class SecuredMethodSecurityConfiguration implements ImportAware, AopInfrastructureBean { - private int interceptorOrderOffset; + private static final Pointcut pointcut = AuthorizationManagerBeforeMethodInterceptor.secured().getPointcut(); + + private final SecuredAuthorizationManager authorizationManager = new SecuredAuthorizationManager(); + + private AuthorizationManagerBeforeMethodInterceptor methodInterceptor = AuthorizationManagerBeforeMethodInterceptor + .secured(this.authorizationManager); @Bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) static MethodInterceptor securedAuthorizationMethodInterceptor( - ObjectProvider strategyProvider, - ObjectProvider eventPublisherProvider, - ObjectProvider registryProvider, ObjectProvider roleHierarchyProvider, - SecuredMethodSecurityConfiguration configuration) { - SecuredAuthorizationManager secured = new SecuredAuthorizationManager(); - AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); - RoleHierarchy roleHierarchy = roleHierarchyProvider.getIfAvailable(NullRoleHierarchy::new); - authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy); - secured.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); - SecurityContextHolderStrategy strategy = strategyProvider - .getIfAvailable(SecurityContextHolder::getContextHolderStrategy); - AuthorizationManager manager = new DeferringObservationAuthorizationManager<>( - registryProvider, secured); - AuthorizationManagerBeforeMethodInterceptor interceptor = AuthorizationManagerBeforeMethodInterceptor - .secured(manager); - interceptor.setOrder(interceptor.getOrder() + configuration.interceptorOrderOffset); - interceptor.setSecurityContextHolderStrategy(strategy); - eventPublisherProvider.ifAvailable(interceptor::setAuthorizationEventPublisher); - return interceptor; + ObjectProvider securedMethodSecurityConfiguration) { + Supplier supplier = () -> { + SecuredMethodSecurityConfiguration configuration = securedMethodSecurityConfiguration.getObject(); + return configuration.methodInterceptor; + }; + return new DeferringMethodInterceptor<>(pointcut, supplier); } @Override public void setImportMetadata(AnnotationMetadata importMetadata) { EnableMethodSecurity annotation = importMetadata.getAnnotations().get(EnableMethodSecurity.class).synthesize(); - this.interceptorOrderOffset = annotation.offset(); + this.methodInterceptor.setOrder(this.methodInterceptor.getOrder() + annotation.offset()); + } + + @Autowired(required = false) + void setRoleHierarchy(RoleHierarchy roleHierarchy) { + AuthoritiesAuthorizationManager authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); + authoritiesAuthorizationManager.setRoleHierarchy(roleHierarchy); + this.authorizationManager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); + } + + @Autowired(required = false) + public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) { + this.methodInterceptor.setSecurityContextHolderStrategy(securityContextHolderStrategy); + } + + @Autowired(required = false) + void setObservationRegistry(ObservationRegistry registry) { + if (registry.isNoop()) { + return; + } + AuthorizationManager observed = new ObservationAuthorizationManager<>(registry, + this.authorizationManager); + this.methodInterceptor = AuthorizationManagerBeforeMethodInterceptor.secured(observed); + } + + @Autowired(required = false) + void setEventPublisher(AuthorizationEventPublisher eventPublisher) { + this.methodInterceptor.setAuthorizationEventPublisher(eventPublisher); } }