diff --git a/core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java b/core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
index 0870fe2da6..2c51a8e6d8 100644
--- a/core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
+++ b/core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
@@ -47,6 +47,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
@@ -111,6 +112,9 @@ public abstract class AbstractSecurityInterceptor
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private ApplicationEventPublisher eventPublisher;
private AccessDecisionManager accessDecisionManager;
@@ -196,7 +200,7 @@ public abstract class AbstractSecurityInterceptor
publishEvent(new PublicInvocationEvent(object));
return null; // no further work post-invocation
}
- if (SecurityContextHolder.getContext().getAuthentication() == null) {
+ if (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {
credentialsNotFound(this.messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"), object, attributes);
}
@@ -216,10 +220,10 @@ public abstract class AbstractSecurityInterceptor
// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
if (runAs != null) {
- SecurityContext origCtx = SecurityContextHolder.getContext();
- SecurityContext newCtx = SecurityContextHolder.createEmptyContext();
+ SecurityContext origCtx = this.securityContextHolderStrategy.getContext();
+ SecurityContext newCtx = this.securityContextHolderStrategy.createEmptyContext();
newCtx.setAuthentication(runAs);
- SecurityContextHolder.setContext(newCtx);
+ this.securityContextHolderStrategy.setContext(newCtx);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
@@ -229,7 +233,7 @@ public abstract class AbstractSecurityInterceptor
}
this.logger.trace("Did not switch RunAs authentication since RunAsManager returned null");
// no further work post-invocation
- return new InterceptorStatusToken(SecurityContextHolder.getContext(), false, attributes, object);
+ return new InterceptorStatusToken(this.securityContextHolderStrategy.getContext(), false, attributes, object);
}
@@ -260,7 +264,7 @@ public abstract class AbstractSecurityInterceptor
*/
protected void finallyInvocation(InterceptorStatusToken token) {
if (token != null && token.isContextHolderRefreshRequired()) {
- SecurityContextHolder.setContext(token.getSecurityContext());
+ this.securityContextHolderStrategy.setContext(token.getSecurityContext());
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.of(
() -> "Reverted to original authentication " + token.getSecurityContext().getAuthentication()));
@@ -305,7 +309,7 @@ public abstract class AbstractSecurityInterceptor
* @return an authenticated Authentication object.
*/
private Authentication authenticateIfRequired() {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication.isAuthenticated() && !this.alwaysReauthenticate) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.format("Did not re-authenticate %s before authorizing", authentication));
@@ -317,9 +321,9 @@ public abstract class AbstractSecurityInterceptor
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
}
- SecurityContext context = SecurityContextHolder.createEmptyContext();
+ SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
- SecurityContextHolder.setContext(context);
+ this.securityContextHolderStrategy.setContext(context);
return authentication;
}
@@ -378,6 +382,17 @@ public abstract class AbstractSecurityInterceptor
public abstract SecurityMetadataSource obtainSecurityMetadataSource();
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
this.accessDecisionManager = accessDecisionManager;
}
diff --git a/core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.java b/core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.java
index f84cfbc43a..46f6a60b4e 100644
--- a/core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.java
+++ b/core/src/test/java/org/springframework/security/core/context/ListeningSecurityContextHolderStrategyTests.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.
diff --git a/etc/checkstyle/checkstyle.xml b/etc/checkstyle/checkstyle.xml
index e8ea50e0d6..ad5192b10f 100644
--- a/etc/checkstyle/checkstyle.xml
+++ b/etc/checkstyle/checkstyle.xml
@@ -15,6 +15,7 @@
+
diff --git a/web/src/main/java/org/springframework/security/web/FilterChainProxy.java b/web/src/main/java/org/springframework/security/web/FilterChainProxy.java
index fe80b492cb..5204f33a39 100644
--- a/web/src/main/java/org/springframework/security/web/FilterChainProxy.java
+++ b/web/src/main/java/org/springframework/security/web/FilterChainProxy.java
@@ -33,6 +33,7 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.HttpStatusRequestRejectedHandler;
@@ -145,6 +146,9 @@ public class FilterChainProxy extends GenericFilterBean {
private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private List filterChains;
private FilterChainValidator filterChainValidator = new NullFilterChainValidator();
@@ -185,7 +189,7 @@ public class FilterChainProxy extends GenericFilterBean {
this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response, ex);
}
finally {
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
@@ -246,6 +250,17 @@ public class FilterChainProxy extends GenericFilterBean {
return Collections.unmodifiableList(this.filterChains);
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
/**
* Used (internally) to specify a validation strategy for the filters in each
* configured chain.
diff --git a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java
index d870e85629..051893c640 100644
--- a/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java
+++ b/web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2021 the original author or authors.
+ * Copyright 2004-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.
@@ -38,6 +38,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
@@ -82,6 +83,9 @@ import org.springframework.web.filter.GenericFilterBean;
*/
public class ExceptionTranslationFilter extends GenericFilterBean implements MessageSourceAware {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private AccessDeniedHandler accessDeniedHandler = new AccessDeniedHandlerImpl();
private AuthenticationEntryPoint authenticationEntryPoint;
@@ -183,7 +187,7 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes
private void handleAccessDeniedException(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, AccessDeniedException exception) throws ServletException, IOException {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
boolean isAnonymous = this.authenticationTrustResolver.isAnonymous(authentication);
if (isAnonymous || this.authenticationTrustResolver.isRememberMe(authentication)) {
if (logger.isTraceEnabled()) {
@@ -209,8 +213,8 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes
AuthenticationException reason) throws ServletException, IOException {
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
// existing Authentication is no longer considered valid
- SecurityContext context = SecurityContextHolder.createEmptyContext();
- SecurityContextHolder.setContext(context);
+ SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
+ this.securityContextHolderStrategy.setContext(context);
this.requestCache.saveRequest(request, response);
this.authenticationEntryPoint.commence(request, response, reason);
}
@@ -239,6 +243,17 @@ public class ExceptionTranslationFilter extends GenericFilterBean implements Mes
this.messages = new MessageSourceAccessor(messageSource);
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
/**
* Default implementation of ThrowableAnalyzer
which is capable of also
* unwrapping ServletException
s.
diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
index 5d00d57292..6fd89a4f10 100644
--- a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
+++ b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java
@@ -34,6 +34,7 @@ import org.springframework.security.authorization.event.AuthorizationDeniedEvent
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -46,6 +47,9 @@ import org.springframework.web.filter.OncePerRequestFilter;
*/
public class AuthorizationFilter extends OncePerRequestFilter {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private final AuthorizationManager authorizationManager;
private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish;
@@ -73,8 +77,19 @@ public class AuthorizationFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response);
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
private Authentication getAuthentication() {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) {
throw new AuthenticationCredentialsNotFoundException(
"An Authentication object was not found in the SecurityContext");
diff --git a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
index e7abefa6fd..df7d7bec51 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
@@ -40,6 +40,7 @@ import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.security.web.context.NullSecurityContextRepository;
@@ -114,6 +115,9 @@ import org.springframework.web.filter.GenericFilterBean;
public abstract class AbstractAuthenticationProcessingFilter extends GenericFilterBean
implements ApplicationEventPublisherAware, MessageSourceAware {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
protected ApplicationEventPublisher eventPublisher;
protected AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
@@ -315,9 +319,9 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
*/
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
Authentication authResult) throws IOException, ServletException {
- SecurityContext context = SecurityContextHolder.createEmptyContext();
+ SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authResult);
- SecurityContextHolder.setContext(context);
+ this.securityContextHolderStrategy.setContext(context);
this.securityContextRepository.saveContext(context, request, response);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
@@ -342,7 +346,7 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
*/
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
AuthenticationException failed) throws IOException, ServletException {
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
this.logger.trace("Failed to process authentication request", failed);
this.logger.trace("Cleared SecurityContextHolder");
this.logger.trace("Handling authentication failure");
@@ -452,6 +456,17 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
this.securityContextRepository = securityContextRepository;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
protected AuthenticationSuccessHandler getSuccessHandler() {
return this.successHandler;
}
diff --git a/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java
index 3b1c628842..ea141bf0da 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java
@@ -34,6 +34,7 @@ import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
@@ -46,6 +47,9 @@ import org.springframework.web.filter.GenericFilterBean;
*/
public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource();
private String key;
@@ -87,14 +91,14 @@ public class AnonymousAuthenticationFilter extends GenericFilterBean implements
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
- if (SecurityContextHolder.getContext().getAuthentication() == null) {
+ if (this.securityContextHolderStrategy.getContext().getAuthentication() == null) {
Authentication authentication = createAuthentication((HttpServletRequest) req);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
+ SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authentication);
- SecurityContextHolder.setContext(context);
+ this.securityContextHolderStrategy.setContext(context);
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
- + SecurityContextHolder.getContext().getAuthentication()));
+ + this.securityContextHolderStrategy.getContext().getAuthentication()));
}
else {
this.logger.debug("Set SecurityContextHolder to anonymous SecurityContext");
@@ -103,7 +107,7 @@ public class AnonymousAuthenticationFilter extends GenericFilterBean implements
else {
if (this.logger.isTraceEnabled()) {
this.logger.trace(LogMessage.of(() -> "Did not set SecurityContextHolder since already authenticated "
- + SecurityContextHolder.getContext().getAuthentication()));
+ + this.securityContextHolderStrategy.getContext().getAuthentication()));
}
}
chain.doFilter(req, res);
@@ -122,6 +126,17 @@ public class AnonymousAuthenticationFilter extends GenericFilterBean implements
this.authenticationDetailsSource = authenticationDetailsSource;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
public Object getPrincipal() {
return this.principal;
}
diff --git a/web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java b/web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java
index 9f987e9197..eaa87f0e70 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/logout/LogoutFilter.java
@@ -28,6 +28,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
@@ -52,6 +53,9 @@ import org.springframework.web.filter.GenericFilterBean;
*/
public class LogoutFilter extends GenericFilterBean {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private RequestMatcher logoutRequestMatcher;
private final LogoutHandler handler;
@@ -92,7 +96,7 @@ public class LogoutFilter extends GenericFilterBean {
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (requiresLogout(request, response)) {
- Authentication auth = SecurityContextHolder.getContext().getAuthentication();
+ Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Logging out [%s]", auth));
}
@@ -119,6 +123,17 @@ public class LogoutFilter extends GenericFilterBean {
return false;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
public void setLogoutRequestMatcher(RequestMatcher logoutRequestMatcher) {
Assert.notNull(logoutRequestMatcher, "logoutRequestMatcher cannot be null");
this.logoutRequestMatcher = logoutRequestMatcher;
diff --git a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java
index c014455bfa..b65e723caf 100644
--- a/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java
+++ b/web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java
@@ -33,6 +33,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.NullRememberMeServices;
import org.springframework.security.web.authentication.RememberMeServices;
@@ -93,6 +94,9 @@ import org.springframework.web.filter.OncePerRequestFilter;
*/
public class BasicAuthenticationFilter extends OncePerRequestFilter {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private AuthenticationEntryPoint authenticationEntryPoint;
private AuthenticationManager authenticationManager;
@@ -170,9 +174,9 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
if (authenticationIsRequired(username)) {
Authentication authResult = this.authenticationManager.authenticate(authRequest);
- SecurityContext context = SecurityContextHolder.createEmptyContext();
+ SecurityContext context = this.securityContextHolderStrategy.createEmptyContext();
context.setAuthentication(authResult);
- SecurityContextHolder.setContext(context);
+ this.securityContextHolderStrategy.setContext(context);
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
}
@@ -182,7 +186,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
}
}
catch (AuthenticationException ex) {
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
this.logger.debug("Failed to process authentication request", ex);
this.rememberMeServices.loginFail(request, response);
onUnsuccessfulAuthentication(request, response, ex);
@@ -201,7 +205,7 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
private boolean authenticationIsRequired(String username) {
// Only reauthenticate if username doesn't match SecurityContextHolder and user
// isn't authenticated (see SEC-53)
- Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
+ Authentication existingAuth = this.securityContextHolderStrategy.getContext().getAuthentication();
if (existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
@@ -242,6 +246,17 @@ public class BasicAuthenticationFilter extends OncePerRequestFilter {
return this.ignoreFailure;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource authenticationDetailsSource) {
this.authenticationConverter.setAuthenticationDetailsSource(authenticationDetailsSource);
diff --git a/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java b/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java
index 27bbe07f3e..052ee25a42 100644
--- a/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.java
+++ b/web/src/main/java/org/springframework/security/web/context/HttpSessionSecurityContextRepository.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.
@@ -89,11 +89,14 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
protected final Log logger = LogFactory.getLog(this.getClass());
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
/**
* SecurityContext instance used to check for equality with default (unauthenticated)
* content
*/
- private final Object contextObject = SecurityContextHolder.createEmptyContext();
+ private Object contextObject = this.securityContextHolderStrategy.createEmptyContext();
private boolean allowSessionCreation = true;
@@ -125,6 +128,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
if (response != null) {
SaveToSessionResponseWrapper wrappedResponse = new SaveToSessionResponseWrapper(response, request,
httpSession != null, context);
+ wrappedResponse.setSecurityContextHolderStrategy(this.securityContextHolderStrategy);
requestResponseHolder.setResponse(wrappedResponse);
requestResponseHolder.setRequest(new SaveToSessionRequestWrapper(request, wrappedResponse));
}
@@ -200,7 +204,7 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
* @return a new SecurityContext instance. Never null.
*/
protected SecurityContext generateNewContext() {
- return SecurityContextHolder.createEmptyContext();
+ return this.securityContextHolderStrategy.createEmptyContext();
}
/**
@@ -236,6 +240,17 @@ public class HttpSessionSecurityContextRepository implements SecurityContextRepo
this.springSecurityContextKey = springSecurityContextKey;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
+ this.securityContextHolderStrategy = strategy;
+ this.contextObject = this.securityContextHolderStrategy.createEmptyContext();
+ }
+
private boolean isTransient(Object object) {
if (object == null) {
return false;
diff --git a/web/src/main/java/org/springframework/security/web/context/NullSecurityContextRepository.java b/web/src/main/java/org/springframework/security/web/context/NullSecurityContextRepository.java
index 697ce743dd..801554edd7 100644
--- a/web/src/main/java/org/springframework/security/web/context/NullSecurityContextRepository.java
+++ b/web/src/main/java/org/springframework/security/web/context/NullSecurityContextRepository.java
@@ -21,6 +21,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
/**
* @author Luke Taylor
@@ -28,6 +29,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
*/
public final class NullSecurityContextRepository implements SecurityContextRepository {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
@Override
public boolean containsContext(HttpServletRequest request) {
return false;
@@ -35,11 +39,21 @@ public final class NullSecurityContextRepository implements SecurityContextRepos
@Override
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
- return SecurityContextHolder.createEmptyContext();
+ return this.securityContextHolderStrategy.createEmptyContext();
}
@Override
public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) {
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strategy) {
+ this.securityContextHolderStrategy = strategy;
+ }
+
}
diff --git a/web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java b/web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java
index 71ff20291c..214ea694f4 100644
--- a/web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.java
+++ b/web/src/main/java/org/springframework/security/web/context/SaveContextOnUpdateOrErrorResponseWrapper.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.
@@ -21,7 +21,9 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.util.OnCommittedResponseWrapper;
+import org.springframework.util.Assert;
/**
* Base class for response wrappers which encapsulate the logic for storing a security
@@ -46,6 +48,9 @@ import org.springframework.security.web.util.OnCommittedResponseWrapper;
@Deprecated
public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommittedResponseWrapper {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private boolean contextSaved = false;
// See SEC-1052
@@ -62,6 +67,17 @@ public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommit
this.disableUrlRewriting = disableUrlRewriting;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
/**
* Invoke this method to disable automatic saving of the {@link SecurityContext} when
* the {@link HttpServletResponse} is committed. This can be useful in the event that
@@ -85,7 +101,7 @@ public abstract class SaveContextOnUpdateOrErrorResponseWrapper extends OnCommit
*/
@Override
protected void onResponseCommitted() {
- saveContext(SecurityContextHolder.getContext());
+ saveContext(this.securityContextHolderStrategy.getContext());
this.contextSaved = true;
}
diff --git a/web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java b/web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java
index 05e3fd493f..e4a8f4eb11 100644
--- a/web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java
+++ b/web/src/main/java/org/springframework/security/web/context/SecurityContextHolderFilter.java
@@ -25,6 +25,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -44,6 +45,9 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
private final SecurityContextRepository securityContextRepository;
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private boolean shouldNotFilterErrorDispatch;
/**
@@ -60,11 +64,11 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
throws ServletException, IOException {
SecurityContext securityContext = this.securityContextRepository.loadContext(request).get();
try {
- SecurityContextHolder.setContext(securityContext);
+ this.securityContextHolderStrategy.setContext(securityContext);
filterChain.doFilter(request, response);
}
finally {
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
}
}
@@ -73,6 +77,17 @@ public class SecurityContextHolderFilter extends OncePerRequestFilter {
return this.shouldNotFilterErrorDispatch;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
/**
* Disables {@link SecurityContextHolderFilter} for error dispatch.
* @param shouldNotFilterErrorDispatch if the Filter should be disabled for error
diff --git a/web/src/main/java/org/springframework/security/web/context/SecurityContextPersistenceFilter.java b/web/src/main/java/org/springframework/security/web/context/SecurityContextPersistenceFilter.java
index c60e6bf45a..76845464d7 100644
--- a/web/src/main/java/org/springframework/security/web/context/SecurityContextPersistenceFilter.java
+++ b/web/src/main/java/org/springframework/security/web/context/SecurityContextPersistenceFilter.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.
@@ -29,6 +29,8 @@ import jakarta.servlet.http.HttpSession;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
+import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;
/**
@@ -66,6 +68,9 @@ public class SecurityContextPersistenceFilter extends GenericFilterBean {
private SecurityContextRepository repo;
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private boolean forceEagerSessionCreation = false;
public SecurityContextPersistenceFilter() {
@@ -99,7 +104,7 @@ public class SecurityContextPersistenceFilter extends GenericFilterBean {
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
- SecurityContextHolder.setContext(contextBeforeChainExecution);
+ this.securityContextHolderStrategy.setContext(contextBeforeChainExecution);
if (contextBeforeChainExecution.getAuthentication() == null) {
logger.debug("Set SecurityContextHolder to empty SecurityContext");
}
@@ -112,9 +117,9 @@ public class SecurityContextPersistenceFilter extends GenericFilterBean {
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
- SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
+ SecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();
// Crucial removal of SecurityContextHolder contents before anything else.
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
@@ -125,4 +130,15 @@ public class SecurityContextPersistenceFilter extends GenericFilterBean {
this.forceEagerSessionCreation = forceEagerSessionCreation;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
}
diff --git a/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java b/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java
index 3f0baa6ebb..0bae10f2cf 100644
--- a/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java
+++ b/web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java
@@ -28,7 +28,9 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.stereotype.Controller;
+import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.support.WebDataBinderFactory;
@@ -88,6 +90,9 @@ import org.springframework.web.method.support.ModelAndViewContainer;
*/
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private ExpressionParser parser = new SpelExpressionParser();
private BeanResolver beanResolver;
@@ -100,7 +105,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication == null) {
return null;
}
@@ -132,6 +137,17 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
this.beanResolver = beanResolver;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
/**
* Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.
* @param annotationClass the class of the {@link Annotation} to find on the
diff --git a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
index 3cae4cf4d1..01ec2059d7 100644
--- a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java
+++ b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.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.
@@ -30,6 +30,7 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
@@ -53,6 +54,9 @@ public class SessionManagementFilter extends GenericFilterBean {
static final String FILTER_APPLIED = "__spring_security_session_mgmt_filter_applied";
+ private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
+ .getContextHolderStrategy();
+
private final SecurityContextRepository securityContextRepository;
private SessionAuthenticationStrategy sessionAuthenticationStrategy;
@@ -89,7 +93,7 @@ public class SessionManagementFilter extends GenericFilterBean {
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (!this.securityContextRepository.containsContext(request)) {
- Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+ Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication != null && !this.trustResolver.isAnonymous(authentication)) {
// The user has been authenticated during the current request, so call the
// session strategy
@@ -99,14 +103,15 @@ public class SessionManagementFilter extends GenericFilterBean {
catch (SessionAuthenticationException ex) {
// The session strategy can reject the authentication
this.logger.debug("SessionAuthenticationStrategy rejected the authentication object", ex);
- SecurityContextHolder.clearContext();
+ this.securityContextHolderStrategy.clearContext();
this.failureHandler.onAuthenticationFailure(request, response, ex);
return;
}
// Eagerly save the security context to make it available for any possible
// re-entrant requests which may occur before the current request
// completes. SEC-1396.
- this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
+ this.securityContextRepository.saveContext(this.securityContextHolderStrategy.getContext(), request,
+ response);
}
else {
// No security context or authentication present. Check for a session
@@ -160,4 +165,15 @@ public class SessionManagementFilter extends GenericFilterBean {
this.trustResolver = trustResolver;
}
+ /**
+ * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
+ * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
+ *
+ * @since 5.8
+ */
+ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
+ Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
+ this.securityContextHolderStrategy = securityContextHolderStrategy;
+ }
+
}
diff --git a/web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java b/web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java
index 30869ec541..cd272305cb 100644
--- a/web/src/test/java/org/springframework/security/web/FilterChainProxyTests.java
+++ b/web/src/test/java/org/springframework/security/web/FilterChainProxyTests.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.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.firewall.FirewalledRequest;
import org.springframework.security.web.firewall.HttpFirewall;
import org.springframework.security.web.firewall.RequestRejectedException;
@@ -197,6 +198,15 @@ public class FilterChainProxyTests {
assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull();
}
+ @Test
+ public void doFilterWhenCustomSecurityContextHolderStrategyClearsSecurityContext() throws Exception {
+ SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
+ this.fcp.setSecurityContextHolderStrategy(strategy);
+ given(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);
+ this.fcp.doFilter(this.request, this.response, this.chain);
+ verify(strategy).clearContext();
+ }
+
@Test
public void doFilterClearsSecurityContextHolderWithException() throws Exception {
given(this.matcher.matches(any(HttpServletRequest.class))).willReturn(true);
diff --git a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java
index c894432198..0b4d640236 100644
--- a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java
@@ -37,6 +37,7 @@ import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.web.util.WebUtils;
@@ -71,9 +72,9 @@ public class AuthorizationFilterTests {
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
- SecurityContext securityContext = new SecurityContextImpl();
- securityContext.setAuthentication(authenticationToken);
- SecurityContextHolder.setContext(securityContext);
+ SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
+ given(strategy.getContext()).willReturn(new SecurityContextImpl(authenticationToken));
+ filter.setSecurityContextHolderStrategy(strategy);
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
@@ -87,6 +88,7 @@ public class AuthorizationFilterTests {
assertThat(authentication.get()).isEqualTo(authenticationToken);
verify(mockFilterChain).doFilter(mockRequest, mockResponse);
+ verify(strategy).getContext();
}
@Test
diff --git a/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java
index 9028998eb1..f45788fefb 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilterTests.java
@@ -34,11 +34,17 @@ import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
+import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
/**
* Tests {@link AnonymousAuthenticationFilter}.
@@ -73,16 +79,19 @@ public class AnonymousAuthenticationFilterTests {
public void testOperationWhenAuthenticationExistsInContextHolder() throws Exception {
// Put an Authentication object into the SecurityContextHolder
Authentication originalAuth = new TestingAuthenticationToken("user", "password", "ROLE_A");
- SecurityContextHolder.getContext().setAuthentication(originalAuth);
+ SecurityContextHolderStrategy strategy = mock(SecurityContextHolderStrategy.class);
+ given(strategy.getContext()).willReturn(new SecurityContextImpl(originalAuth));
AnonymousAuthenticationFilter filter = new AnonymousAuthenticationFilter("qwerty", "anonymousUsername",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
+ filter.setSecurityContextHolderStrategy(strategy);
// Test
MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("x");
executeFilterInContainerSimulator(mock(FilterConfig.class), filter, request, new MockHttpServletResponse(),
new MockFilterChain(true));
// Ensure filter didn't change our original object
- assertThat(SecurityContextHolder.getContext().getAuthentication()).isEqualTo(originalAuth);
+ verify(strategy).getContext();
+ verify(strategy, never()).setContext(any());
}
@Test
diff --git a/web/src/test/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilterTests.java
index 9487957fa5..890dbcb576 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/UsernamePasswordAuthenticationFilterTests.java
@@ -17,20 +17,28 @@
package org.springframework.security.web.authentication;
import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.stubbing.Answer;
+import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContext;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
/**
* Tests {@link UsernamePasswordAuthenticationFilter}.
@@ -118,6 +126,22 @@ public class UsernamePasswordAuthenticationFilterTests {
.isThrownBy(() -> filter.attemptAuthentication(request, new MockHttpServletResponse()));
}
+ @Test
+ public void testSecurityContextHolderStrategyUsed() throws Exception {
+ MockHttpServletRequest request = new MockHttpServletRequest("POST", "/login");
+ request.setServletPath("/login");
+ request.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_USERNAME_KEY, "rod");
+ request.addParameter(UsernamePasswordAuthenticationFilter.SPRING_SECURITY_FORM_PASSWORD_KEY, "koala");
+ UsernamePasswordAuthenticationFilter filter = new UsernamePasswordAuthenticationFilter();
+ filter.setAuthenticationManager(createAuthenticationManager());
+ SecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());
+ filter.setSecurityContextHolderStrategy(strategy);
+ filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
+ ArgumentCaptor captor = ArgumentCaptor.forClass(SecurityContext.class);
+ verify(strategy).setContext(captor.capture());
+ assertThat(captor.getValue().getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);
+ }
+
/**
* SEC-571
*/
diff --git a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
index c6a52952bd..0315dae9f0 100644
--- a/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilterTests.java
@@ -27,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
+import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -37,6 +38,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.test.web.CodecTestUtils;
import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.security.web.context.SecurityContextRepository;
@@ -50,6 +52,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
@@ -145,6 +148,19 @@ public class BasicAuthenticationFilterTests {
assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("rod");
}
+ @Test
+ public void testSecurityContextHolderStrategyUsed() throws Exception {
+ String token = "rod:koala";
+ MockHttpServletRequest request = new MockHttpServletRequest();
+ request.addHeader("Authorization", "Basic " + CodecTestUtils.encodeBase64(token.getBytes()));
+ SecurityContextHolderStrategy strategy = spy(SecurityContextHolder.getContextHolderStrategy());
+ this.filter.setSecurityContextHolderStrategy(strategy);
+ this.filter.doFilter(request, new MockHttpServletResponse(), new MockFilterChain());
+ ArgumentCaptor captor = ArgumentCaptor.forClass(SecurityContext.class);
+ verify(strategy).setContext(captor.capture());
+ assertThat(captor.getValue().getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class);
+ }
+
// gh-5586
@Test
public void doFilterWhenSchemeLowercaseThenCaseInsensitveMatchWorks() throws Exception {
diff --git a/web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java b/web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java
index f169591c0d..04e56e4ee2 100644
--- a/web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/context/SecurityContextHolderFilterTests.java
@@ -32,10 +32,12 @@ import org.springframework.security.authentication.TestAuthentication;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.context.SecurityContextImpl;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
+import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class SecurityContextHolderFilterTests {
@@ -43,6 +45,9 @@ class SecurityContextHolderFilterTests {
@Mock
private SecurityContextRepository repository;
+ @Mock
+ private SecurityContextHolderStrategy strategy;
+
@Mock
private HttpServletRequest request;
@@ -77,6 +82,21 @@ class SecurityContextHolderFilterTests {
assertThat(SecurityContextHolder.getContext()).isEqualTo(SecurityContextHolder.createEmptyContext());
}
+ @Test
+ void doFilterThenSetsAndClearsSecurityContextHolderStrategy() throws Exception {
+ Authentication authentication = TestAuthentication.authenticatedUser();
+ SecurityContext expectedContext = new SecurityContextImpl(authentication);
+ given(this.repository.loadContext(this.requestArg.capture())).willReturn(() -> expectedContext);
+ FilterChain filterChain = (request, response) -> {
+ };
+
+ this.filter.setSecurityContextHolderStrategy(this.strategy);
+ this.filter.doFilter(this.request, this.response, filterChain);
+
+ verify(this.strategy).setContext(expectedContext);
+ verify(this.strategy).clearContext();
+ }
+
@Test
void shouldNotFilterErrorDispatchWhenDefault() {
assertThat(this.filter.shouldNotFilterErrorDispatch()).isFalse();