Skip to content

Commit 68f29bc

Browse files
committed
Add SecurityContextHolderStrategy to Default Components
Issue spring-projectsgh-11060
1 parent 79c2b87 commit 68f29bc

23 files changed

+362
-52
lines changed

core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.springframework.security.core.SpringSecurityMessageSource;
4848
import org.springframework.security.core.context.SecurityContext;
4949
import org.springframework.security.core.context.SecurityContextHolder;
50+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
5051
import org.springframework.util.Assert;
5152
import org.springframework.util.CollectionUtils;
5253

@@ -111,6 +112,9 @@ public abstract class AbstractSecurityInterceptor
111112

112113
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
113114

115+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
116+
.getContextHolderStrategy();
117+
114118
private ApplicationEventPublisher eventPublisher;
115119

116120
private AccessDecisionManager accessDecisionManager;
@@ -196,7 +200,7 @@ protected InterceptorStatusToken beforeInvocation(Object object) {
196200
publishEvent(new PublicInvocationEvent(object));
197201
return null; // no further work post-invocation
198202
}
199-
if (SecurityContextHolder.getContext().getAuthentication() == null) {
203+
if (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {
200204
credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
201205
"An Authentication object was not found in the SecurityContext"), object, attributes);
202206
}
@@ -216,10 +220,10 @@ protected InterceptorStatusToken beforeInvocation(Object object) {
216220
// Attempt to run as a different user
217221
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
218222
if (runAs != null) {
219-
SecurityContext origCtx = SecurityContextHolder.getContext();
220-
SecurityContext newCtx = SecurityContextHolder.createEmptyContext();
223+
SecurityContext origCtx = this.securityContextHolderStrategy.getContext();
224+
SecurityContext newCtx = this.securityContextHolderStrategy.createEmptyContext();
221225
newCtx.setAuthentication(runAs);
222-
SecurityContextHolder.setContext(newCtx);
226+
this.securityContextHolderStrategy.setContext(newCtx);
223227

224228
if (this.logger.isDebugEnabled()) {
225229
this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
@@ -229,7 +233,7 @@ protected InterceptorStatusToken beforeInvocation(Object object) {
229233
}
230234
this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
231235
// no further work post-invocation
232-
return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
236+
return new InterceptorStatusToken(this.securityContextHolderStrategy.getContext(), false, attributes, object);
233237

234238
}
235239

@@ -260,7 +264,7 @@ else if (this.logger.isDebugEnabled()) {
260264
*/
261265
protected void finallyInvocation(InterceptorStatusToken token) {
262266
if (token != null && token.isContextHolderRefreshRequired()) {
263-
SecurityContextHolder.setContext(token.getSecurityContext());
267+
this.securityContextHolderStrategy.setContext(token.getSecurityContext());
264268
if (this.logger.isDebugEnabled()) {
265269
this.logger.debug(LogMessage.of(
266270
() -> "Reverted to original authentication " + token.getSecurityContext().getAuthentication()));
@@ -305,7 +309,7 @@ protected Object afterInvocation(InterceptorStatusToken token, Object returnedOb
305309
* @return an authenticated <tt>Authentication</tt> object.
306310
*/
307311
private Authentication authenticateIfRequired() {
308-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
312+
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
309313
if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
310314
if (this.logger.isTraceEnabled()) {
311315
this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication));
@@ -317,9 +321,9 @@ private Authentication authenticateIfRequired() {
317321
if (this.logger.isDebugEnabled()) {
318322
this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
319323
}
320-
SecurityContext context = SecurityContextHolder.createEmptyContext();
324+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
321325
context.setAuthentication(authentication);
322-
SecurityContextHolder.setContext(context);
326+
this.securityContextHolderStrategy.setContext(context);
323327
return authentication;
324328
}
325329

@@ -378,6 +382,17 @@ public boolean isValidateConfigAttributes() {
378382

379383
public abstract SecurityMetadataSource obtainSecurityMetadataSource();
380384

385+
/**
386+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
387+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
388+
*
389+
* @since 5.8
390+
*/
391+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
392+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
393+
this.securityContextHolderStrategy = securityContextHolderStrategy;
394+
}
395+
381396
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
382397
this.accessDecisionManager = accessDecisionManager;
383398
}

core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.java

Lines changed: 1 addition & 1 deletion
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.

etc/checkstyle/checkstyle.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<property name="excludes" value="io.spring.javaformat.checkstyle.check.SpringHeaderCheck" />
1616
<property name="avoidStaticImportExcludes" value="org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.*" />
1717
<property name="avoidStaticImportExcludes" value="org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*" />
18+
<property name="avoidStaticImportExcludes" value="org.springframework.security.config.annotation.SecurityContextChangedListenerArgumentMatchers.*" />
1819
</module>
1920
<module name="com.puppycrawl.tools.checkstyle.TreeWalker">
2021
<module name="com.puppycrawl.tools.checkstyle.checks.regexp.RegexpSinglelineJavaCheck">

web/src/main/java/org/springframework/security/web/FilterChainProxy.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
import org.springframework.core.log.LogMessage;
3636
import org.springframework.security.core.context.SecurityContextHolder;
37+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3738
import org.springframework.security.web.firewall.DefaultRequestRejectedHandler;
3839
import org.springframework.security.web.firewall.FirewalledRequest;
3940
import org.springframework.security.web.firewall.HttpFirewall;
@@ -146,6 +147,9 @@ public class FilterChainProxy extends GenericFilterBean {
146147

147148
private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");
148149

150+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
151+
.getContextHolderStrategy();
152+
149153
private List<SecurityFilterChain> filterChains;
150154

151155
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
@@ -186,7 +190,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha
186190
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
187191
}
188192
finally {
189-
SecurityContextHolder.clearContext();
193+
this.securityContextHolderStrategy.clearContext();
190194
request.removeAttribute(FILTER_APPLIED);
191195
}
192196
}
@@ -247,6 +251,17 @@ public List<SecurityFilterChain> getFilterChains() {
247251
return Collections.unmodifiableList(this.filterChains);
248252
}
249253

254+
/**
255+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
256+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
257+
*
258+
* @since 5.8
259+
*/
260+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
261+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
262+
this.securityContextHolderStrategy = securityContextHolderStrategy;
263+
}
264+
250265
/**
251266
* Used (internally) to specify a validation strategy for the filters in each
252267
* configured chain.

web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2004-2021 the original author or authors.
2+
* Copyright 2004-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.
@@ -38,6 +38,7 @@
3838
import org.springframework.security.core.SpringSecurityMessageSource;
3939
import org.springframework.security.core.context.SecurityContext;
4040
import org.springframework.security.core.context.SecurityContextHolder;
41+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4142
import org.springframework.security.web.AuthenticationEntryPoint;
4243
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
4344
import org.springframework.security.web.savedrequest.RequestCache;
@@ -82,6 +83,9 @@
8283
*/
8384
public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {
8485

86+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
87+
.getContextHolderStrategy();
88+
8589
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
8690

8791
private AuthenticationEntryPoint authenticationEntryPoint;
@@ -183,7 +187,7 @@ private void handleAuthenticationException(HttpServletRequest request, HttpServl
183187

184188
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
185189
FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
186-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
190+
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
187191
boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);
188192
if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
189193
if (logger.isTraceEnabled()) {
@@ -209,8 +213,8 @@ protected void sendStartAuthentication(HttpServletRequest request, HttpServletRe
209213
AuthenticationException reason) throws ServletException, IOException {
210214
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
211215
// existing Authentication is no longer considered valid
212-
SecurityContext context = SecurityContextHolder.createEmptyContext();
213-
SecurityContextHolder.setContext(context);
216+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
217+
this.securityContextHolderStrategy.setContext(context);
214218
this.requestCache.saveRequest(request, response);
215219
this.authenticationEntryPoint.commence(request, response, reason);
216220
}
@@ -239,6 +243,17 @@ public void setMessageSource(MessageSource messageSource) {
239243
this.messages = new MessageSourceAccessor(messageSource);
240244
}
241245

246+
/**
247+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
248+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
249+
*
250+
* @since 5.8
251+
*/
252+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
253+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
254+
this.securityContextHolderStrategy = securityContextHolderStrategy;
255+
}
256+
242257
/**
243258
* Default implementation of <code>ThrowableAnalyzer</code> which is capable of also
244259
* unwrapping <code>ServletException</code>s.

web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
3535
import org.springframework.security.core.Authentication;
3636
import org.springframework.security.core.context.SecurityContextHolder;
37+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3738
import org.springframework.util.Assert;
3839
import org.springframework.web.filter.OncePerRequestFilter;
3940

@@ -46,6 +47,9 @@
4647
*/
4748
public class AuthorizationFilter extends OncePerRequestFilter {
4849

50+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
51+
.getContextHolderStrategy();
52+
4953
private final AuthorizationManager<HttpServletRequest> authorizationManager;
5054

5155
private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish;
@@ -73,8 +77,19 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
7377
filterChain.doFilter(request, response);
7478
}
7579

80+
/**
81+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
82+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
83+
*
84+
* @since 5.8
85+
*/
86+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
87+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
88+
this.securityContextHolderStrategy = securityContextHolderStrategy;
89+
}
90+
7691
private Authentication getAuthentication() {
77-
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
92+
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
7893
if (authentication == null) {
7994
throw new AuthenticationCredentialsNotFoundException(
8095
"An Authentication object was not found in the SecurityContext");

web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.springframework.security.core.SpringSecurityMessageSource;
4141
import org.springframework.security.core.context.SecurityContext;
4242
import org.springframework.security.core.context.SecurityContextHolder;
43+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
4344
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
4445
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
4546
import org.springframework.security.web.context.NullSecurityContextRepository;
@@ -114,6 +115,9 @@
114115
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
115116
implements ApplicationEventPublisherAware, MessageSourceAware {
116117

118+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
119+
.getContextHolderStrategy();
120+
117121
protected ApplicationEventPublisher eventPublisher;
118122

119123
protected AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
@@ -315,9 +319,9 @@ public abstract Authentication attemptAuthentication(HttpServletRequest request,
315319
*/
316320
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
317321
Authentication authResult) throws IOException, ServletException {
318-
SecurityContext context = SecurityContextHolder.createEmptyContext();
322+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
319323
context.setAuthentication(authResult);
320-
SecurityContextHolder.setContext(context);
324+
this.securityContextHolderStrategy.setContext(context);
321325
this.securityContextRepository.saveContext(context, request, response);
322326
if (this.logger.isDebugEnabled()) {
323327
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
@@ -342,7 +346,7 @@ protected void successfulAuthentication(HttpServletRequest request, HttpServletR
342346
*/
343347
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
344348
AuthenticationException failed) throws IOException, ServletException {
345-
SecurityContextHolder.clearContext();
349+
this.securityContextHolderStrategy.clearContext();
346350
this.logger.trace("Failed to process authentication request", failed);
347351
this.logger.trace("Cleared SecurityContextHolder");
348352
this.logger.trace("Handling authentication failure");
@@ -452,6 +456,17 @@ public void setSecurityContextRepository(SecurityContextRepository securityConte
452456
this.securityContextRepository = securityContextRepository;
453457
}
454458

459+
/**
460+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
461+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
462+
*
463+
* @since 5.8
464+
*/
465+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
466+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
467+
this.securityContextHolderStrategy = securityContextHolderStrategy;
468+
}
469+
455470
protected AuthenticationSuccessHandler getSuccessHandler() {
456471
return this.successHandler;
457472
}

web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.springframework.security.core.authority.AuthorityUtils;
3535
import org.springframework.security.core.context.SecurityContext;
3636
import org.springframework.security.core.context.SecurityContextHolder;
37+
import org.springframework.security.core.context.SecurityContextHolderStrategy;
3738
import org.springframework.util.Assert;
3839
import org.springframework.web.filter.GenericFilterBean;
3940

@@ -46,6 +47,9 @@
4647
*/
4748
public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
4849

50+
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
51+
.getContextHolderStrategy();
52+
4953
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new WebAuthenticationDetailsSource();
5054

5155
private String key;
@@ -87,14 +91,14 @@ public void afterPropertiesSet() {
8791
@Override
8892
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
8993
throws IOException, ServletException {
90-
if (SecurityContextHolder.getContext().getAuthentication() == null) {
94+
if (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {
9195
Authentication authentication = createAuthentication((HttpServletRequest) req);
92-
SecurityContext context = SecurityContextHolder.createEmptyContext();
96+
SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
9397
context.setAuthentication(authentication);
94-
SecurityContextHolder.setContext(context);
98+
this.securityContextHolderStrategy.setContext(context);
9599
if (this.logger.isTraceEnabled()) {
96100
this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
97-
+ SecurityContextHolder.getContext().getAuthentication()));
101+
+ this.securityContextHolderStrategy.getContext().getAuthentication()));
98102
}
99103
else {
100104
this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
@@ -103,7 +107,7 @@ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
103107
else {
104108
if (this.logger.isTraceEnabled()) {
105109
this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
106-
+ SecurityContextHolder.getContext().getAuthentication()));
110+
+ this.securityContextHolderStrategy.getContext().getAuthentication()));
107111
}
108112
}
109113
chain.doFilter(req, res);
@@ -122,6 +126,17 @@ public void setAuthenticationDetailsSource(
122126
this.authenticationDetailsSource = authenticationDetailsSource;
123127
}
124128

129+
/**
130+
* Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
131+
* the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
132+
*
133+
* @since 5.8
134+
*/
135+
public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
136+
Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
137+
this.securityContextHolderStrategy = securityContextHolderStrategy;
138+
}
139+
125140
public Object getPrincipal() {
126141
return this.principal;
127142
}

0 commit comments

Comments
 (0)