Skip to content

Commit 5540bbc

Browse files
evgeniychebanjzheaux
authored andcommitted
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
1 parent 2be141f commit 5540bbc

File tree

18 files changed

+236
-44
lines changed

18 files changed

+236
-44
lines changed

core/src/main/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,6 +35,7 @@
3535
* objects.
3636
*
3737
* @author Luke Taylor
38+
* @author Evgeniy Cheban
3839
* @since 3.1
3940
*/
4041
public abstract class AbstractSecurityExpressionHandler<T>
@@ -116,6 +117,10 @@ public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
116117
this.permissionEvaluator = permissionEvaluator;
117118
}
118119

120+
protected BeanResolver getBeanResolver() {
121+
return this.beanResolver;
122+
}
123+
119124
@Override
120125
public void setApplicationContext(ApplicationContext applicationContext) {
121126
this.beanResolver = new BeanFactoryResolver(applicationContext);

core/src/main/java/org/springframework/security/access/expression/SecurityExpressionHandler.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.access.expression;
1818

19+
import java.util.function.Supplier;
20+
1921
import org.springframework.aop.framework.AopInfrastructureBean;
2022
import org.springframework.expression.EvaluationContext;
2123
import org.springframework.expression.ExpressionParser;
@@ -26,6 +28,7 @@
2628
* expressions from the implementation of the underlying expression objects
2729
*
2830
* @author Luke Taylor
31+
* @author Evgeniy Cheban
2932
* @since 3.1
3033
*/
3134
public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
@@ -41,4 +44,19 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
4144
*/
4245
EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
4346

47+
/**
48+
* Provides an evaluation context in which to evaluate security expressions for the
49+
* invocation type. You can override this method in order to provide a custom
50+
* implementation that uses lazy initialization of the {@link Authentication} object.
51+
* By default, this method uses eager initialization of the {@link Authentication}
52+
* object.
53+
* @param authentication the {@link Supplier} of the {@link Authentication} to use
54+
* @param invocation the {@link T} to use
55+
* @return the {@link EvaluationContext} to use
56+
* @since 5.8
57+
*/
58+
default EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, T invocation) {
59+
return createEvaluationContext(authentication.get(), invocation);
60+
}
61+
4462
}

core/src/main/java/org/springframework/security/access/expression/SecurityExpressionRoot.java

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,23 +19,26 @@
1919
import java.io.Serializable;
2020
import java.util.Collection;
2121
import java.util.Set;
22+
import java.util.function.Supplier;
2223

2324
import org.springframework.security.access.PermissionEvaluator;
2425
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
2526
import org.springframework.security.authentication.AuthenticationTrustResolver;
2627
import org.springframework.security.core.Authentication;
2728
import org.springframework.security.core.GrantedAuthority;
2829
import org.springframework.security.core.authority.AuthorityUtils;
30+
import org.springframework.util.Assert;
2931

3032
/**
3133
* Base root object for use in Spring Security expression evaluations.
3234
*
3335
* @author Luke Taylor
36+
* @author Evgeniy Cheban
3437
* @since 3.0
3538
*/
3639
public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
3740

38-
protected final Authentication authentication;
41+
private final Supplier<Authentication> authentication;
3942

4043
private AuthenticationTrustResolver trustResolver;
4144

@@ -72,10 +75,18 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
7275
* @param authentication the {@link Authentication} to use. Cannot be null.
7376
*/
7477
public SecurityExpressionRoot(Authentication authentication) {
75-
if (authentication == null) {
76-
throw new IllegalArgumentException("Authentication object cannot be null");
77-
}
78-
this.authentication = authentication;
78+
this(() -> authentication);
79+
}
80+
81+
/**
82+
* Creates a new instance that uses lazy initialization of the {@link Authentication}
83+
* object.
84+
* @param authentication the {@link Supplier} of the {@link Authentication} to use.
85+
* Cannot be null.
86+
* @since 5.8
87+
*/
88+
public SecurityExpressionRoot(Supplier<Authentication> authentication) {
89+
this.authentication = new AuthenticationSupplier(authentication);
7990
}
8091

8192
@Override
@@ -111,7 +122,7 @@ private boolean hasAnyAuthorityName(String prefix, String... roles) {
111122

112123
@Override
113124
public final Authentication getAuthentication() {
114-
return this.authentication;
125+
return this.authentication.get();
115126
}
116127

117128
@Override
@@ -126,7 +137,7 @@ public final boolean denyAll() {
126137

127138
@Override
128139
public final boolean isAnonymous() {
129-
return this.trustResolver.isAnonymous(this.authentication);
140+
return this.trustResolver.isAnonymous(getAuthentication());
130141
}
131142

132143
@Override
@@ -136,13 +147,13 @@ public final boolean isAuthenticated() {
136147

137148
@Override
138149
public final boolean isRememberMe() {
139-
return this.trustResolver.isRememberMe(this.authentication);
150+
return this.trustResolver.isRememberMe(getAuthentication());
140151
}
141152

142153
@Override
143154
public final boolean isFullyAuthenticated() {
144-
return !this.trustResolver.isAnonymous(this.authentication)
145-
&& !this.trustResolver.isRememberMe(this.authentication);
155+
Authentication authentication = getAuthentication();
156+
return !this.trustResolver.isAnonymous(authentication) && !this.trustResolver.isRememberMe(authentication);
146157
}
147158

148159
/**
@@ -151,7 +162,7 @@ public final boolean isFullyAuthenticated() {
151162
* @return
152163
*/
153164
public Object getPrincipal() {
154-
return this.authentication.getPrincipal();
165+
return getAuthentication().getPrincipal();
155166
}
156167

157168
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
@@ -181,7 +192,7 @@ public void setDefaultRolePrefix(String defaultRolePrefix) {
181192

182193
private Set<String> getAuthoritySet() {
183194
if (this.roles == null) {
184-
Collection<? extends GrantedAuthority> userAuthorities = this.authentication.getAuthorities();
195+
Collection<? extends GrantedAuthority> userAuthorities = getAuthentication().getAuthorities();
185196
if (this.roleHierarchy != null) {
186197
userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
187198
}
@@ -192,12 +203,12 @@ private Set<String> getAuthoritySet() {
192203

193204
@Override
194205
public boolean hasPermission(Object target, Object permission) {
195-
return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
206+
return this.permissionEvaluator.hasPermission(getAuthentication(), target, permission);
196207
}
197208

198209
@Override
199210
public boolean hasPermission(Object targetId, String targetType, Object permission) {
200-
return this.permissionEvaluator.hasPermission(this.authentication, (Serializable) targetId, targetType,
211+
return this.permissionEvaluator.hasPermission(getAuthentication(), (Serializable) targetId, targetType,
201212
permission);
202213
}
203214

@@ -225,4 +236,27 @@ private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String
225236
return defaultRolePrefix + role;
226237
}
227238

239+
private static final class AuthenticationSupplier implements Supplier<Authentication> {
240+
241+
private Authentication value;
242+
243+
private final Supplier<Authentication> delegate;
244+
245+
private AuthenticationSupplier(Supplier<Authentication> delegate) {
246+
Assert.notNull(delegate, "delegate cannot be null");
247+
this.delegate = delegate;
248+
}
249+
250+
@Override
251+
public Authentication get() {
252+
if (this.value == null) {
253+
Authentication authentication = this.delegate.get();
254+
Assert.notNull(authentication, "Authentication object cannot be null");
255+
this.value = authentication;
256+
}
257+
return this.value;
258+
}
259+
260+
}
261+
228262
}

core/src/main/java/org/springframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
import java.util.LinkedHashMap;
2424
import java.util.List;
2525
import java.util.Map;
26+
import java.util.function.Supplier;
2627
import java.util.stream.Stream;
2728

2829
import org.aopalliance.intercept.MethodInvocation;
@@ -50,6 +51,7 @@
5051
* support.
5152
*
5253
* @author Luke Taylor
54+
* @author Evgeniy Cheban
5355
* @since 3.0
5456
*/
5557
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
@@ -77,12 +79,26 @@ public StandardEvaluationContext createEvaluationContextInternal(Authentication
7779
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
7880
}
7981

82+
@Override
83+
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
84+
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
85+
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
86+
getParameterNameDiscoverer());
87+
ctx.setBeanResolver(getBeanResolver());
88+
return ctx;
89+
}
90+
8091
/**
8192
* Creates the root object for expression evaluation.
8293
*/
8394
@Override
8495
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
8596
MethodInvocation invocation) {
97+
return createSecurityExpressionRoot(() -> authentication, invocation);
98+
}
99+
100+
private MethodSecurityExpressionOperations createSecurityExpressionRoot(Supplier<Authentication> authentication,
101+
MethodInvocation invocation) {
86102
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
87103
root.setThis(invocation.getThis());
88104
root.setPermissionEvaluator(getPermissionEvaluator());

core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,6 +34,7 @@
3434
*
3535
* @author Luke Taylor
3636
* @author Daniel Bustamante
37+
* @author Evgeniy Cheban
3738
* @since 3.0
3839
*/
3940
class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
@@ -52,6 +53,11 @@ class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
5253
super(mi.getThis(), getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
5354
}
5455

56+
MethodSecurityEvaluationContext(MethodSecurityExpressionOperations root, MethodInvocation mi,
57+
ParameterNameDiscoverer parameterNameDiscoverer) {
58+
super(root, getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
59+
}
60+
5561
private static Method getSpecificMethod(MethodInvocation mi) {
5662
return AopUtils.getMostSpecificMethod(mi.getMethod(), AopProxyUtils.ultimateTargetClass(mi.getThis()));
5763
}

core/src/main/java/org/springframework/security/access/expression/method/MethodSecurityExpressionRoot.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,13 +16,16 @@
1616

1717
package org.springframework.security.access.expression.method;
1818

19+
import java.util.function.Supplier;
20+
1921
import org.springframework.security.access.expression.SecurityExpressionRoot;
2022
import org.springframework.security.core.Authentication;
2123

2224
/**
2325
* Extended expression root object which contains extra method-specific functionality.
2426
*
2527
* @author Luke Taylor
28+
* @author Evgeniy Cheban
2629
* @since 3.0
2730
*/
2831
class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
@@ -37,6 +40,10 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
3740
super(a);
3841
}
3942

43+
MethodSecurityExpressionRoot(Supplier<Authentication> authentication) {
44+
super(authentication);
45+
}
46+
4047
@Override
4148
public void setFilterObject(Object filterObject) {
4249
this.filterObject = filterObject;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Meth
7272
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
7373
return null;
7474
}
75-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
75+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication,
7676
mi.getMethodInvocation());
7777
this.expressionHandler.setReturnObject(mi.getResult(), ctx);
7878
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -128,7 +128,7 @@ public Object invoke(MethodInvocation mi) throws Throwable {
128128
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
129129
return returnedObject;
130130
}
131-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
131+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
132132
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
133133
}
134134

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -72,7 +72,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, Meth
7272
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
7373
return null;
7474
}
75-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
75+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
7676
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
7777
return new ExpressionAttributeAuthorizationDecision(granted, attribute);
7878
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -126,7 +126,7 @@ public Object invoke(MethodInvocation mi) throws Throwable {
126126
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
127127
return mi.proceed();
128128
}
129-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
129+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
130130
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
131131
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
132132
return mi.proceed();

0 commit comments

Comments
 (0)