瀏覽代碼

createEvaluationContext should defer lookup of Authentication

- Added createEvaluationContext method that accepts Supplier<Authentication>
- Refactored classes that use EvaluationContext to use lazy initialization of Authentication

Closes gh-9667
Evgeniy Cheban 3 年之前
父節點
當前提交
5540bbcf0b
共有 18 個文件被更改,包括 236 次插入44 次删除
  1. 6 1
      core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java
  2. 19 1
      core/src/main/java/org/springframework/security/access/expression/SecurityExpressionHandler.java
  3. 49 15
      core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java
  4. 17 1
      core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java
  5. 7 1
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java
  6. 8 1
      core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java
  7. 2 2
      core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java
  8. 2 2
      core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java
  9. 2 2
      core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java
  10. 2 2
      core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java
  11. 20 1
      core/src/test/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandlerTests.java
  12. 19 1
      messaging/src/main/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandler.java
  13. 15 1
      messaging/src/main/java/org/springframework/security/messaging/access/expression/MessageSecurityExpressionRoot.java
  14. 21 1
      messaging/src/test/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandlerTests.java
  15. 19 0
      web/src/main/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.java
  16. 1 5
      web/src/main/java/org/springframework/security/web/access/expression/WebExpressionAuthorizationManager.java
  17. 7 6
      web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.java
  18. 20 1
      web/src/test/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandlerTests.java

+ 6 - 1
core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -35,6 +35,7 @@ import org.springframework.util.Assert;
  * objects.
  *
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  * @since 3.1
  */
 public abstract class AbstractSecurityExpressionHandler<T>
@@ -116,6 +117,10 @@ public abstract class AbstractSecurityExpressionHandler<T>
 		this.permissionEvaluator = permissionEvaluator;
 	}
 
+	protected BeanResolver getBeanResolver() {
+		return this.beanResolver;
+	}
+
 	@Override
 	public void setApplicationContext(ApplicationContext applicationContext) {
 		this.beanResolver = new BeanFactoryResolver(applicationContext);

+ 19 - 1
core/src/main/java/org/springframework/security/access/expression/SecurityExpressionHandler.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression;
 
+import java.util.function.Supplier;
+
 import org.springframework.aop.framework.AopInfrastructureBean;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.ExpressionParser;
@@ -26,6 +28,7 @@ import org.springframework.security.core.Authentication;
  * expressions from the implementation of the underlying expression objects
  *
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  * @since 3.1
  */
 public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
@@ -41,4 +44,19 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
 	 */
 	EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
 
+	/**
+	 * Provides an evaluation context in which to evaluate security expressions for the
+	 * invocation type. You can override this method in order to provide a custom
+	 * implementation that uses lazy initialization of the {@link Authentication} object.
+	 * By default, this method uses eager initialization of the {@link Authentication}
+	 * object.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to use
+	 * @param invocation the {@link T} to use
+	 * @return the {@link EvaluationContext} to use
+	 * @since 5.8
+	 */
+	default EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, T invocation) {
+		return createEvaluationContext(authentication.get(), invocation);
+	}
+
 }

+ 49 - 15
core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -19,6 +19,7 @@ package org.springframework.security.access.expression;
 import java.io.Serializable;
 import java.util.Collection;
 import java.util.Set;
+import java.util.function.Supplier;
 
 import org.springframework.security.access.PermissionEvaluator;
 import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
@@ -26,16 +27,18 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.util.Assert;
 
 /**
  * Base root object for use in Spring Security expression evaluations.
  *
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  * @since 3.0
  */
 public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
 
-	protected final Authentication authentication;
+	private final Supplier<Authentication> authentication;
 
 	private AuthenticationTrustResolver trustResolver;
 
@@ -72,10 +75,18 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	 * @param authentication the {@link Authentication} to use. Cannot be null.
 	 */
 	public SecurityExpressionRoot(Authentication authentication) {
-		if (authentication == null) {
-			throw new IllegalArgumentException("Authentication object cannot be null");
-		}
-		this.authentication = authentication;
+		this(() -> authentication);
+	}
+
+	/**
+	 * Creates a new instance that uses lazy initialization of the {@link Authentication}
+	 * object.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to use.
+	 * Cannot be null.
+	 * @since 5.8
+	 */
+	public SecurityExpressionRoot(Supplier<Authentication> authentication) {
+		this.authentication = new AuthenticationSupplier(authentication);
 	}
 
 	@Override
@@ -111,7 +122,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	@Override
 	public final Authentication getAuthentication() {
-		return this.authentication;
+		return this.authentication.get();
 	}
 
 	@Override
@@ -126,7 +137,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	@Override
 	public final boolean isAnonymous() {
-		return this.trustResolver.isAnonymous(this.authentication);
+		return this.trustResolver.isAnonymous(getAuthentication());
 	}
 
 	@Override
@@ -136,13 +147,13 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	@Override
 	public final boolean isRememberMe() {
-		return this.trustResolver.isRememberMe(this.authentication);
+		return this.trustResolver.isRememberMe(getAuthentication());
 	}
 
 	@Override
 	public final boolean isFullyAuthenticated() {
-		return !this.trustResolver.isAnonymous(this.authentication)
-				&& !this.trustResolver.isRememberMe(this.authentication);
+		Authentication authentication = getAuthentication();
+		return !this.trustResolver.isAnonymous(authentication) && !this.trustResolver.isRememberMe(authentication);
 	}
 
 	/**
@@ -151,7 +162,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 	 * @return
 	 */
 	public Object getPrincipal() {
-		return this.authentication.getPrincipal();
+		return getAuthentication().getPrincipal();
 	}
 
 	public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
@@ -181,7 +192,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	private Set<String> getAuthoritySet() {
 		if (this.roles == null) {
-			Collection<? extends GrantedAuthority> userAuthorities = this.authentication.getAuthorities();
+			Collection<? extends GrantedAuthority> userAuthorities = getAuthentication().getAuthorities();
 			if (this.roleHierarchy != null) {
 				userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
 			}
@@ -192,12 +203,12 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 
 	@Override
 	public boolean hasPermission(Object target, Object permission) {
-		return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
+		return this.permissionEvaluator.hasPermission(getAuthentication(), target, permission);
 	}
 
 	@Override
 	public boolean hasPermission(Object targetId, String targetType, Object permission) {
-		return this.permissionEvaluator.hasPermission(this.authentication, (Serializable) targetId, targetType,
+		return this.permissionEvaluator.hasPermission(getAuthentication(), (Serializable) targetId, targetType,
 				permission);
 	}
 
@@ -225,4 +236,27 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
 		return defaultRolePrefix + role;
 	}
 
+	private static final class AuthenticationSupplier implements Supplier<Authentication> {
+
+		private Authentication value;
+
+		private final Supplier<Authentication> delegate;
+
+		private AuthenticationSupplier(Supplier<Authentication> delegate) {
+			Assert.notNull(delegate, "delegate cannot be null");
+			this.delegate = delegate;
+		}
+
+		@Override
+		public Authentication get() {
+			if (this.value == null) {
+				Authentication authentication = this.delegate.get();
+				Assert.notNull(authentication, "Authentication object cannot be null");
+				this.value = authentication;
+			}
+			return this.value;
+		}
+
+	}
+
 }

+ 17 - 1
core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -23,6 +23,7 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 import java.util.stream.Stream;
 
 import org.aopalliance.intercept.MethodInvocation;
@@ -50,6 +51,7 @@ import org.springframework.util.Assert;
  * support.
  *
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  * @since 3.0
  */
 public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
@@ -77,12 +79,26 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
 		return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
 	}
 
+	@Override
+	public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
+		MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
+		MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
+				getParameterNameDiscoverer());
+		ctx.setBeanResolver(getBeanResolver());
+		return ctx;
+	}
+
 	/**
 	 * Creates the root object for expression evaluation.
 	 */
 	@Override
 	protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
 			MethodInvocation invocation) {
+		return createSecurityExpressionRoot(() -> authentication, invocation);
+	}
+
+	private MethodSecurityExpressionOperations createSecurityExpressionRoot(Supplier<Authentication> authentication,
+			MethodInvocation invocation) {
 		MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
 		root.setThis(invocation.getThis());
 		root.setPermissionEvaluator(getPermissionEvaluator());

+ 7 - 1
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -34,6 +34,7 @@ import org.springframework.security.core.parameters.DefaultSecurityParameterName
  *
  * @author Luke Taylor
  * @author Daniel Bustamante
+ * @author Evgeniy Cheban
  * @since 3.0
  */
 class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
@@ -52,6 +53,11 @@ class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
 		super(mi.getThis(), getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
 	}
 
+	MethodSecurityEvaluationContext(MethodSecurityExpressionOperations root, MethodInvocation mi,
+			ParameterNameDiscoverer parameterNameDiscoverer) {
+		super(root, getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
+	}
+
 	private static Method getSpecificMethod(MethodInvocation mi) {
 		return AopUtils.getMostSpecificMethod(mi.getMethod(), AopProxyUtils.ultimateTargetClass(mi.getThis()));
 	}

+ 8 - 1
core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.access.expression.method;
 
+import java.util.function.Supplier;
+
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.core.Authentication;
 
@@ -23,6 +25,7 @@ import org.springframework.security.core.Authentication;
  * Extended expression root object which contains extra method-specific functionality.
  *
  * @author Luke Taylor
+ * @author Evgeniy Cheban
  * @since 3.0
  */
 class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
@@ -37,6 +40,10 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
 		super(a);
 	}
 
+	MethodSecurityExpressionRoot(Supplier<Authentication> authentication) {
+		super(authentication);
+	}
+
 	@Override
 	public void setFilterObject(Object filterObject) {
 		this.filterObject = filterObject;

+ 2 - 2
core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -72,7 +72,7 @@ public final class PostAuthorizeAuthorizationManager implements AuthorizationMan
 		if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
 			return null;
 		}
-		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
+		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication,
 				mi.getMethodInvocation());
 		this.expressionHandler.setReturnObject(mi.getResult(), ctx);
 		boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);

+ 2 - 2
core/src/main/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -128,7 +128,7 @@ public final class PostFilterAuthorizationMethodInterceptor
 		if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
 			return returnedObject;
 		}
-		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
+		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
 		return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
 	}
 

+ 2 - 2
core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeAuthorizationManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -72,7 +72,7 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
 		if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
 			return null;
 		}
-		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
+		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
 		boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
 		return new ExpressionAttributeAuthorizationDecision(granted, attribute);
 	}

+ 2 - 2
core/src/main/java/org/springframework/security/authorization/method/PreFilterAuthorizationMethodInterceptor.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2021 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -126,7 +126,7 @@ public final class PreFilterAuthorizationMethodInterceptor
 		if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
 			return mi.proceed();
 		}
-		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
+		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
 		Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
 		this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
 		return mi.proceed();

+ 20 - 1
core/src/test/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandlerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2020 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -19,10 +19,12 @@ package org.springframework.security.access.expression.method;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Supplier;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import org.aopalliance.intercept.MethodInvocation;
+import org.assertj.core.api.InstanceOfAssertFactories;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -32,6 +34,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
+import org.springframework.expression.TypedValue;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -43,6 +47,7 @@ import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 @ExtendWith(MockitoExtension.class)
 public class DefaultMethodSecurityExpressionHandlerTests {
@@ -167,6 +172,20 @@ public class DefaultMethodSecurityExpressionHandlerTests {
 		verify(upstream).close();
 	}
 
+	@Test
+	public void createEvaluationContextSupplierAuthentication() {
+		setupMocks();
+		Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
+		given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
+		EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier,
+				this.methodInvocation);
+		verifyNoInteractions(mockAuthenticationSupplier);
+		assertThat(context.getRootObject()).extracting(TypedValue::getValue)
+				.asInstanceOf(InstanceOfAssertFactories.type(MethodSecurityExpressionRoot.class))
+				.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
+		verify(mockAuthenticationSupplier).get();
+	}
+
 	static class Foo {
 
 		void bar() {

+ 19 - 1
messaging/src/main/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandler.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,10 @@
 
 package org.springframework.security.messaging.access.expression;
 
+import java.util.function.Supplier;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.messaging.Message;
 import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
@@ -31,15 +35,29 @@ import org.springframework.util.Assert;
  *
  * @param <T> the type for the body of the Message
  * @author Rob Winch
+ * @author Evgeniy Cheban
  * @since 4.0
  */
 public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {
 
 	private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
 
+	@Override
+	public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, Message<T> message) {
+		MessageSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, message);
+		StandardEvaluationContext ctx = new StandardEvaluationContext(root);
+		ctx.setBeanResolver(getBeanResolver());
+		return ctx;
+	}
+
 	@Override
 	protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
 			Message<T> invocation) {
+		return createSecurityExpressionRoot(() -> authentication, invocation);
+	}
+
+	private MessageSecurityExpressionRoot createSecurityExpressionRoot(Supplier<Authentication> authentication,
+			Message<T> invocation) {
 		MessageSecurityExpressionRoot root = new MessageSecurityExpressionRoot(authentication, invocation);
 		root.setPermissionEvaluator(getPermissionEvaluator());
 		root.setTrustResolver(this.trustResolver);

+ 15 - 1
messaging/src/main/java/org/springframework/security/messaging/access/expression/MessageSecurityExpressionRoot.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,8 @@
 
 package org.springframework.security.messaging.access.expression;
 
+import java.util.function.Supplier;
+
 import org.springframework.messaging.Message;
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.core.Authentication;
@@ -24,6 +26,7 @@ import org.springframework.security.core.Authentication;
  * The {@link SecurityExpressionRoot} used for {@link Message} expressions.
  *
  * @author Rob Winch
+ * @author Evgeniy Cheban
  * @since 4.0
  */
 public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
@@ -31,6 +34,17 @@ public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
 	public final Message<?> message;
 
 	public MessageSecurityExpressionRoot(Authentication authentication, Message<?> message) {
+		this(() -> authentication, message);
+	}
+
+	/**
+	 * Creates an instance for the given {@link Supplier} of the {@link Authentication}
+	 * and {@link Message}.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to use
+	 * @param message the {@link Message} to use
+	 * @since 5.8
+	 */
+	public MessageSecurityExpressionRoot(Supplier<Authentication> authentication, Message<?> message) {
 		super(authentication);
 		this.message = message;
 	}

+ 21 - 1
messaging/src/test/java/org/springframework/security/messaging/access/expression/DefaultMessageSecurityExpressionHandlerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2013 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,9 @@
 
 package org.springframework.security.messaging.access.expression;
 
+import java.util.function.Supplier;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -24,10 +27,12 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
+import org.springframework.expression.TypedValue;
 import org.springframework.messaging.Message;
 import org.springframework.messaging.support.GenericMessage;
 import org.springframework.security.access.PermissionEvaluator;
 import org.springframework.security.access.expression.ExpressionUtils;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
@@ -38,6 +43,9 @@ import org.springframework.security.core.authority.AuthorityUtils;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
 import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 @ExtendWith(MockitoExtension.class)
 public class DefaultMessageSecurityExpressionHandlerTests {
@@ -104,4 +112,16 @@ public class DefaultMessageSecurityExpressionHandlerTests {
 		assertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isTrue();
 	}
 
+	@Test
+	public void createEvaluationContextSupplierAuthentication() {
+		Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
+		given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
+		EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.message);
+		verifyNoInteractions(mockAuthenticationSupplier);
+		assertThat(context.getRootObject()).extracting(TypedValue::getValue)
+				.asInstanceOf(InstanceOfAssertFactories.type(MessageSecurityExpressionRoot.class))
+				.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
+		verify(mockAuthenticationSupplier).get();
+	}
+
 }

+ 19 - 0
web/src/main/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandler.java

@@ -16,6 +16,10 @@
 
 package org.springframework.security.web.access.expression;
 
+import java.util.function.Supplier;
+
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
 import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionHandler;
 import org.springframework.security.access.expression.SecurityExpressionOperations;
@@ -39,9 +43,24 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
 
 	private String defaultRolePrefix = "ROLE_";
 
+	@Override
+	public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
+			RequestAuthorizationContext context) {
+		WebSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, context);
+		StandardEvaluationContext ctx = new StandardEvaluationContext(root);
+		ctx.setBeanResolver(getBeanResolver());
+		context.getVariables().forEach(ctx::setVariable);
+		return ctx;
+	}
+
 	@Override
 	protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
 			RequestAuthorizationContext context) {
+		return createSecurityExpressionRoot(() -> authentication, context);
+	}
+
+	private WebSecurityExpressionRoot createSecurityExpressionRoot(Supplier<Authentication> authentication,
+			RequestAuthorizationContext context) {
 		WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context.getRequest());
 		root.setRoleHierarchy(getRoleHierarchy());
 		root.setPermissionEvaluator(getPermissionEvaluator());

+ 1 - 5
web/src/main/java/org/springframework/security/web/access/expression/WebExpressionAuthorizationManager.java

@@ -16,7 +16,6 @@
 
 package org.springframework.security.web.access.expression;
 
-import java.util.Map;
 import java.util.function.Supplier;
 
 import org.springframework.expression.EvaluationContext;
@@ -72,10 +71,7 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan
 	 */
 	@Override
 	public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
-		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), context);
-		for (Map.Entry<String, String> entry : context.getVariables().entrySet()) {
-			ctx.setVariable(entry.getKey(), entry.getValue());
-		}
+		EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);
 		boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);
 		return new ExpressionAuthorizationDecision(granted, this.expression);
 	}

+ 7 - 6
web/src/main/java/org/springframework/security/web/access/expression/WebSecurityExpressionRoot.java

@@ -16,8 +16,9 @@
 
 package org.springframework.security.web.access.expression;
 
-import jakarta.servlet.http.HttpServletRequest;
+import java.util.function.Supplier;
 
+import jakarta.servlet.http.HttpServletRequest;
 import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.web.FilterInvocation;
@@ -36,17 +37,17 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
 	public final HttpServletRequest request;
 
 	public WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
-		this(a, fi.getRequest());
+		this(() -> a, fi.getRequest());
 	}
 
 	/**
-	 * Creates an instance for the given {@link Authentication} and
-	 * {@link HttpServletRequest}.
-	 * @param authentication the {@link Authentication} to use
+	 * Creates an instance for the given {@link Supplier} of the {@link Authentication}
+	 * and {@link HttpServletRequest}.
+	 * @param authentication the {@link Supplier} of the {@link Authentication} to use
 	 * @param request the {@link HttpServletRequest} to use
 	 * @since 5.8
 	 */
-	public WebSecurityExpressionRoot(Authentication authentication, HttpServletRequest request) {
+	public WebSecurityExpressionRoot(Supplier<Authentication> authentication, HttpServletRequest request) {
 		super(authentication);
 		this.request = request;
 	}

+ 20 - 1
web/src/test/java/org/springframework/security/web/access/expression/DefaultHttpSecurityExpressionHandlerTests.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2022 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.
@@ -16,6 +16,9 @@
 
 package org.springframework.security.web.access.expression;
 
+import java.util.function.Supplier;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -28,7 +31,9 @@ import org.springframework.context.support.StaticApplicationContext;
 import org.springframework.expression.EvaluationContext;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.TypedValue;
 import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.access.expression.SecurityExpressionRoot;
 import org.springframework.security.authentication.AuthenticationTrustResolver;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
@@ -36,8 +41,10 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
 
 @ExtendWith(MockitoExtension.class)
 public class DefaultHttpSecurityExpressionHandlerTests {
@@ -91,4 +98,16 @@ public class DefaultHttpSecurityExpressionHandlerTests {
 		verify(this.trustResolver).isAnonymous(this.authentication);
 	}
 
+	@Test
+	public void createEvaluationContextSupplierAuthentication() {
+		Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
+		given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
+		EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.context);
+		verifyNoInteractions(mockAuthenticationSupplier);
+		assertThat(context.getRootObject()).extracting(TypedValue::getValue)
+				.asInstanceOf(InstanceOfAssertFactories.type(WebSecurityExpressionRoot.class))
+				.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
+		verify(mockAuthenticationSupplier).get();
+	}
+
 }