1
0
mirror of synced 2026-08-05 17:57:15 +00:00

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
This commit is contained in:
Evgeniy Cheban
2022-05-07 21:28:54 +03:00
committed by Josh Cummings
parent 2be141f800
commit 5540bbcf0b
18 changed files with 236 additions and 44 deletions
@@ -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());
@@ -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);
}
@@ -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;
}
@@ -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();
}
}