Skip to content

Commit 25c7489

Browse files
committed
Add SecurityContextHolderStrategy to Method Security
Issue gh-11060
1 parent f86992a commit 25c7489

8 files changed

+206
-45
lines changed

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

+26-10
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.security.authorization.AuthorizationManager;
3838
import org.springframework.security.core.Authentication;
3939
import org.springframework.security.core.context.SecurityContextHolder;
40+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4041
import org.springframework.util.Assert;
4142

4243
/**
@@ -50,14 +51,8 @@
5051
public final class AuthorizationManagerAfterMethodInterceptor
5152
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
5253

53-
static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
54-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
55-
if (authentication == null) {
56-
throw new AuthenticationCredentialsNotFoundException(
57-
"An Authentication object was not found in the SecurityContext");
58-
}
59-
return authentication;
60-
};
54+
private Supplier<Authentication> authentication = getAuthentication(
55+
SecurityContextHolder.getContextHolderStrategy());
6156

6257
private final Log logger = LogFactory.getLog(this.getClass());
6358

@@ -154,11 +149,21 @@ public boolean isPerInstance() {
154149
return true;
155150
}
156151

152+
/**
153+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
154+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
155+
*
156+
* @since 5.8
157+
*/
158+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
159+
this.authentication = getAuthentication(strategy);
160+
}
161+
157162
private void attemptAuthorization(MethodInvocation mi, Object result) {
158163
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
159164
MethodInvocationResult object = new MethodInvocationResult(mi, result);
160-
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, object);
161-
this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, object, decision);
165+
AuthorizationDecision decision = this.authorizationManager.check(this.authentication, object);
166+
this.eventPublisher.publishAuthorizationEvent(this.authentication, object, decision);
162167
if (decision != null && !decision.isGranted()) {
163168
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
164169
+ this.authorizationManager + " and decision " + decision));
@@ -167,6 +172,17 @@ private void attemptAuthorization(MethodInvocation mi, Object result) {
167172
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
168173
}
169174

175+
private Supplier<Authentication> getAuthentication(SecurityContextHolderStrategy strategy) {
176+
return () -> {
177+
Authentication authentication = strategy.getContext().getAuthentication();
178+
if (authentication == null) {
179+
throw new AuthenticationCredentialsNotFoundException(
180+
"An Authentication object was not found in the SecurityContext");
181+
}
182+
return authentication;
183+
};
184+
}
185+
170186
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
171187
AuthorizationDecision decision) {
172188

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

+26-10
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.security.authorization.AuthorizationManager;
4343
import org.springframework.security.core.Authentication;
4444
import org.springframework.security.core.context.SecurityContextHolder;
45+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4546
import org.springframework.util.Assert;
4647

4748
/**
@@ -55,14 +56,8 @@
5556
public final class AuthorizationManagerBeforeMethodInterceptor
5657
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
5758

58-
static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
59-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
60-
if (authentication == null) {
61-
throw new AuthenticationCredentialsNotFoundException(
62-
"An Authentication object was not found in the SecurityContext");
63-
}
64-
return authentication;
65-
};
59+
private Supplier<Authentication> authentication = getAuthentication(
60+
SecurityContextHolder.getContextHolderStrategy());
6661

6762
private final Log logger = LogFactory.getLog(this.getClass());
6863

@@ -200,10 +195,20 @@ public boolean isPerInstance() {
200195
return true;
201196
}
202197

198+
/**
199+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
200+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
201+
*
202+
* @since 5.8
203+
*/
204+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
205+
this.authentication = getAuthentication(securityContextHolderStrategy);
206+
}
207+
203208
private void attemptAuthorization(MethodInvocation mi) {
204209
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
205-
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, mi);
206-
this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, mi, decision);
210+
AuthorizationDecision decision = this.authorizationManager.check(this.authentication, mi);
211+
this.eventPublisher.publishAuthorizationEvent(this.authentication, mi, decision);
207212
if (decision != null && !decision.isGranted()) {
208213
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
209214
+ this.authorizationManager + " and decision " + decision));
@@ -212,6 +217,17 @@ private void attemptAuthorization(MethodInvocation mi) {
212217
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
213218
}
214219

220+
private Supplier<Authentication> getAuthentication(SecurityContextHolderStrategy strategy) {
221+
return () -> {
222+
Authentication authentication = strategy.getContext().getAuthentication();
223+
if (authentication == null) {
224+
throw new AuthenticationCredentialsNotFoundException(
225+
"An Authentication object was not found in the SecurityContext");
226+
}
227+
return authentication;
228+
};
229+
}
230+
215231
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
216232
AuthorizationDecision decision) {
217233

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

+25-9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
3838
import org.springframework.security.core.Authentication;
3939
import org.springframework.security.core.context.SecurityContextHolder;
40+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4041
import org.springframework.util.Assert;
4142

4243
/**
@@ -51,14 +52,8 @@
5152
public final class PostFilterAuthorizationMethodInterceptor
5253
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
5354

54-
private static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
55-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
56-
if (authentication == null) {
57-
throw new AuthenticationCredentialsNotFoundException(
58-
"An Authentication object was not found in the SecurityContext");
59-
}
60-
return authentication;
61-
};
55+
private Supplier<Authentication> authentication = getAuthentication(
56+
SecurityContextHolder.getContextHolderStrategy());
6257

6358
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
6459

@@ -115,6 +110,16 @@ public boolean isPerInstance() {
115110
return true;
116111
}
117112

113+
/**
114+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
115+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
116+
*
117+
* @since 5.8
118+
*/
119+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
120+
this.authentication = getAuthentication(strategy);
121+
}
122+
118123
/**
119124
* Filter a {@code returnedObject} using the {@link PostFilter} annotation that the
120125
* {@link MethodInvocation} specifies.
@@ -128,10 +133,21 @@ public Object invoke(MethodInvocation mi) throws Throwable {
128133
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
129134
return returnedObject;
130135
}
131-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
136+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(this.authentication, mi);
132137
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
133138
}
134139

140+
private Supplier<Authentication> getAuthentication(SecurityContextHolderStrategy strategy) {
141+
return () -> {
142+
Authentication authentication = strategy.getContext().getAuthentication();
143+
if (authentication == null) {
144+
throw new AuthenticationCredentialsNotFoundException(
145+
"An Authentication object was not found in the SecurityContext");
146+
}
147+
return authentication;
148+
};
149+
}
150+
135151
private final class PostFilterExpressionAttributeRegistry
136152
extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
137153

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

+25-9
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
3838
import org.springframework.security.core.Authentication;
3939
import org.springframework.security.core.context.SecurityContextHolder;
40+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4041
import org.springframework.util.Assert;
4142
import org.springframework.util.StringUtils;
4243

@@ -51,14 +52,8 @@
5152
public final class PreFilterAuthorizationMethodInterceptor
5253
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
5354

54-
private static final Supplier<Authentication> AUTHENTICATION_SUPPLIER = () -> {
55-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
56-
if (authentication == null) {
57-
throw new AuthenticationCredentialsNotFoundException(
58-
"An Authentication object was not found in the SecurityContext");
59-
}
60-
return authentication;
61-
};
55+
private Supplier<Authentication> authentication = getAuthentication(
56+
SecurityContextHolder.getContextHolderStrategy());
6257

6358
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
6459

@@ -115,6 +110,16 @@ public boolean isPerInstance() {
115110
return true;
116111
}
117112

113+
/**
114+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
115+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
116+
*
117+
* @since 5.8
118+
*/
119+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
120+
this.authentication = getAuthentication(strategy);
121+
}
122+
118123
/**
119124
* Filter the method argument specified in the {@link PreFilter} annotation that
120125
* {@link MethodInvocation} specifies.
@@ -126,7 +131,7 @@ public Object invoke(MethodInvocation mi) throws Throwable {
126131
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
127132
return mi.proceed();
128133
}
129-
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
134+
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(this.authentication, mi);
130135
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
131136
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
132137
return mi.proceed();
@@ -152,6 +157,17 @@ private Object findFilterTarget(String filterTargetName, EvaluationContext ctx,
152157
return filterTarget;
153158
}
154159

160+
private Supplier<Authentication> getAuthentication(SecurityContextHolderStrategy strategy) {
161+
return () -> {
162+
Authentication authentication = strategy.getContext().getAuthentication();
163+
if (authentication == null) {
164+
throw new AuthenticationCredentialsNotFoundException(
165+
"An Authentication object was not found in the SecurityContext");
166+
}
167+
return authentication;
168+
};
169+
}
170+
155171
private final class PreFilterExpressionAttributeRegistry
156172
extends AbstractExpressionAttributeRegistry<PreFilterExpressionAttribute> {
157173

core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java

+19-3
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,21 @@
2222
import org.junit.jupiter.api.Test;
2323

2424
import org.springframework.aop.Pointcut;
25+
import org.springframework.security.authentication.TestAuthentication;
2526
import org.springframework.security.authentication.TestingAuthenticationToken;
2627
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
2728
import org.springframework.security.authorization.AuthorizationDecision;
2829
import org.springframework.security.authorization.AuthorizationEventPublisher;
2930
import org.springframework.security.authorization.AuthorizationManager;
31+
import org.springframework.security.core.Authentication;
3032
import org.springframework.security.core.context.SecurityContext;
3133
import org.springframework.security.core.context.SecurityContextHolder;
34+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3235
import org.springframework.security.core.context.SecurityContextImpl;
3336

3437
import static org.assertj.core.api.Assertions.assertThat;
3538
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3639
import static org.mockito.ArgumentMatchers.any;
37-
import static org.mockito.ArgumentMatchers.eq;
3840
import static org.mockito.BDDMockito.given;
3941
import static org.mockito.Mockito.mock;
4042
import static org.mockito.Mockito.verify;
@@ -71,8 +73,22 @@ public void beforeWhenMockAuthorizationManagerThenCheckAndReturnedObject() throw
7173
Pointcut.TRUE, mockAuthorizationManager);
7274
Object returnedObject = advice.invoke(mockMethodInvocation);
7375
assertThat(returnedObject).isEqualTo(result.getResult());
74-
verify(mockAuthorizationManager).check(eq(AuthorizationManagerAfterMethodInterceptor.AUTHENTICATION_SUPPLIER),
75-
any(MethodInvocationResult.class));
76+
verify(mockAuthorizationManager).check(any(Supplier.class), any(MethodInvocationResult.class));
77+
}
78+
79+
@Test
80+
public void afterWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {
81+
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
82+
Authentication authentication = TestAuthentication.authenticatedUser();
83+
given(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));
84+
MethodInvocation invocation = mock(MethodInvocation.class);
85+
AuthorizationManager<MethodInvocationResult> authorizationManager = AuthenticatedAuthorizationManager
86+
.authenticated();
87+
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
88+
Pointcut.TRUE, authorizationManager);
89+
advice.setSecurityContextHolderStrategy(strategy);
90+
advice.invoke(invocation);
91+
verify(strategy).getContext();
7692
}
7793

7894
@Test

core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727
import org.springframework.security.authorization.AuthorizationDecision;
2828
import org.springframework.security.authorization.AuthorizationEventPublisher;
2929
import org.springframework.security.authorization.AuthorizationManager;
30+
import org.springframework.security.core.Authentication;
31+
import org.springframework.security.core.authority.AuthorityUtils;
3032
import org.springframework.security.core.context.SecurityContext;
3133
import org.springframework.security.core.context.SecurityContextHolder;
34+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3235
import org.springframework.security.core.context.SecurityContextImpl;
3336

3437
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3538
import static org.mockito.ArgumentMatchers.any;
39+
import static org.mockito.ArgumentMatchers.eq;
3640
import static org.mockito.BDDMockito.given;
3741
import static org.mockito.Mockito.mock;
3842
import static org.mockito.Mockito.verify;
@@ -66,8 +70,22 @@ public void beforeWhenMockAuthorizationManagerThenCheck() throws Throwable {
6670
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
6771
Pointcut.TRUE, mockAuthorizationManager);
6872
advice.invoke(mockMethodInvocation);
69-
verify(mockAuthorizationManager).check(AuthorizationManagerBeforeMethodInterceptor.AUTHENTICATION_SUPPLIER,
70-
mockMethodInvocation);
73+
verify(mockAuthorizationManager).check(any(Supplier.class), eq(mockMethodInvocation));
74+
}
75+
76+
@Test
77+
public void beforeWhenMockSecurityContextHolderStrategyThenUses() throws Throwable {
78+
SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
79+
Authentication authentication = new TestingAuthenticationToken("user", "password",
80+
AuthorityUtils.createAuthorityList("authority"));
81+
given(strategy.getContext()).willReturn(new SecurityContextImpl(authentication));
82+
MethodInvocation invocation = mock(MethodInvocation.class);
83+
AuthorizationManager<MethodInvocation> authorizationManager = AuthenticatedAuthorizationManager.authenticated();
84+
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
85+
Pointcut.TRUE, authorizationManager);
86+
advice.setSecurityContextHolderStrategy(strategy);
87+
advice.invoke(invocation);
88+
verify(strategy).getContext();
7189
}
7290

7391
@Test

0 commit comments

Comments
 (0)