From 8dbf354d14126e9c8bd6e9574b8cda07b73c73e0 Mon Sep 17 00:00:00 2001 From: Marcus Da Coregio Date: Tue, 12 Sep 2023 18:51:10 +0100 Subject: [PATCH] Defer MethodSecurityExpressionHandler Resolution When using Spring Security ACL and compiling to Native, in order to create the '*AuthorizationMethodInterceptor' Proxy beans during build time, Spring tries to resolve the DataSource bean since the DataSource can be a dependency of some AclService implementations, and fails because some required data source properties are not available during build time. This commit defers the initialization of the MethodSecurityExpressionHandler to the runtime. Closes gh-12653 --- .../PrePostMethodSecurityConfiguration.java | 55 +++++++++++++++++-- 1 file changed, 51 insertions(+), 4 deletions(-) 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 c3bb88c8f4c..be9dcfbb433 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,8 +16,11 @@ 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.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanDefinition; @@ -25,6 +28,9 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.authorization.AuthorizationEventPublisher; @@ -36,7 +42,9 @@ import org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager; import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor; import org.springframework.security.config.core.GrantedAuthorityDefaults; +import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolderStrategy; +import org.springframework.util.function.SingletonSupplier; /** * Base {@link Configuration} for enabling Spring Security Method Security. @@ -59,7 +67,7 @@ static MethodInterceptor preFilterAuthorizationMethodInterceptor( PreFilterAuthorizationMethodInterceptor preFilter = new PreFilterAuthorizationMethodInterceptor(); strategyProvider.ifAvailable(preFilter::setSecurityContextHolderStrategy); preFilter.setExpressionHandler( - expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context))); + new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context)); return preFilter; } @@ -73,7 +81,7 @@ static MethodInterceptor preAuthorizeAuthorizationMethodInterceptor( ObjectProvider registryProvider, ApplicationContext context) { PreAuthorizeAuthorizationManager manager = new PreAuthorizeAuthorizationManager(); manager.setExpressionHandler( - expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context))); + new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context)); AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor .preAuthorize(manager(manager, registryProvider)); strategyProvider.ifAvailable(preAuthorize::setSecurityContextHolderStrategy); @@ -91,7 +99,7 @@ static MethodInterceptor postAuthorizeAuthorizationMethodInterceptor( ObjectProvider registryProvider, ApplicationContext context) { PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager(); manager.setExpressionHandler( - expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context))); + new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context)); AuthorizationManagerAfterMethodInterceptor postAuthorize = AuthorizationManagerAfterMethodInterceptor .postAuthorize(manager(manager, registryProvider)); strategyProvider.ifAvailable(postAuthorize::setSecurityContextHolderStrategy); @@ -108,7 +116,7 @@ static MethodInterceptor postFilterAuthorizationMethodInterceptor( PostFilterAuthorizationMethodInterceptor postFilter = new PostFilterAuthorizationMethodInterceptor(); strategyProvider.ifAvailable(postFilter::setSecurityContextHolderStrategy); postFilter.setExpressionHandler( - expressionHandlerProvider.getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, context))); + new DeferringMethodSecurityExpressionHandler(expressionHandlerProvider, defaultsProvider, context)); return postFilter; } @@ -125,4 +133,43 @@ static AuthorizationManager manager(AuthorizationManager delegate, return new DeferringObservationAuthorizationManager<>(registryProvider, delegate); } + private static final class DeferringMethodSecurityExpressionHandler implements MethodSecurityExpressionHandler { + + private final Supplier expressionHandler; + + private DeferringMethodSecurityExpressionHandler( + ObjectProvider expressionHandlerProvider, + ObjectProvider defaultsProvider, ApplicationContext applicationContext) { + this.expressionHandler = SingletonSupplier.of(() -> expressionHandlerProvider + .getIfAvailable(() -> defaultExpressionHandler(defaultsProvider, applicationContext))); + } + + @Override + public ExpressionParser getExpressionParser() { + return this.expressionHandler.get().getExpressionParser(); + } + + @Override + public EvaluationContext createEvaluationContext(Authentication authentication, MethodInvocation invocation) { + return this.expressionHandler.get().createEvaluationContext(authentication, invocation); + } + + @Override + public EvaluationContext createEvaluationContext(Supplier authentication, + MethodInvocation invocation) { + return this.expressionHandler.get().createEvaluationContext(authentication, invocation); + } + + @Override + public Object filter(Object filterTarget, Expression filterExpression, EvaluationContext ctx) { + return this.expressionHandler.get().filter(filterTarget, filterExpression, ctx); + } + + @Override + public void setReturnObject(Object returnObject, EvaluationContext ctx) { + this.expressionHandler.get().setReturnObject(returnObject, ctx); + } + + } + }