Bladeren bron

Immutable SecurityContext

Issue gh-10032
Josh Cummings 4 jaren geleden
bovenliggende
commit
b8d51725c7
15 gewijzigde bestanden met toevoegingen van 66 en 22 verwijderingen
  1. 4 1
      cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java
  2. 6 3
      core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java
  3. 3 1
      core/src/main/java/org/springframework/security/core/Authentication.java
  4. 6 2
      core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java
  5. 4 1
      remoting/src/main/java/org/springframework/security/remoting/rmi/ContextPropagatingRemoteInvocation.java
  6. 4 2
      web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java
  7. 4 1
      web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java
  8. 5 1
      web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java
  9. 4 1
      web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java
  10. 5 2
      web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java
  11. 4 1
      web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java
  12. 7 2
      web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java
  13. 4 1
      web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java
  14. 2 1
      web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java
  15. 4 2
      web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java

+ 4 - 1
cas/src/main/java/org/springframework/security/cas/web/CasAuthenticationFilter.java

@@ -37,6 +37,7 @@ import org.springframework.security.cas.web.authentication.ServiceAuthentication
 import org.springframework.security.cas.web.authentication.ServiceAuthenticationDetailsSource;
 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.web.authentication.AbstractAuthenticationProcessingFilter;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -219,7 +220,9 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
 		}
 		this.logger.debug(
 				LogMessage.format("Authentication success. Updating SecurityContextHolder to contain: %s", authResult));
-		SecurityContextHolder.getContext().setAuthentication(authResult);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authResult);
+		SecurityContextHolder.setContext(context);
 		if (this.eventPublisher != null) {
 			this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
 		}

+ 6 - 3
core/src/main/java/org/springframework/security/access/intercept/AbstractSecurityInterceptor.java

@@ -217,8 +217,9 @@ public abstract class AbstractSecurityInterceptor
 		Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attributes);
 		if (runAs != null) {
 			SecurityContext origCtx = SecurityContextHolder.getContext();
-			SecurityContextHolder.setContext(SecurityContextHolder.createEmptyContext());
-			SecurityContextHolder.getContext().setAuthentication(runAs);
+			SecurityContext newCtx = SecurityContextHolder.createEmptyContext();
+			newCtx.setAuthentication(runAs);
+			SecurityContextHolder.setContext(newCtx);
 
 			if (this.logger.isDebugEnabled()) {
 				this.logger.debug(LogMessage.format("Switched to RunAs authentication %s", runAs));
@@ -316,7 +317,9 @@ public abstract class AbstractSecurityInterceptor
 		if (this.logger.isDebugEnabled()) {
 			this.logger.debug(LogMessage.format("Re-authenticated %s before authorizing", authentication));
 		}
-		SecurityContextHolder.getContext().setAuthentication(authentication);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authentication);
+		SecurityContextHolder.setContext(context);
 		return authentication;
 	}
 

+ 3 - 1
core/src/main/java/org/springframework/security/core/Authentication.java

@@ -36,7 +36,9 @@ import org.springframework.security.core.context.SecurityContextHolder;
  * the code:
  *
  * <pre>
- * SecurityContextHolder.getContext().setAuthentication(anAuthentication);
+ * SecurityContext context = SecurityContextHolder.createEmptyContext();
+ * context.setAuthentication(anAuthentication);
+ * SecurityContextHolder.setContext(context);
  * </pre>
  *
  * Note that unless the <tt>Authentication</tt> has the <tt>authenticated</tt> property

+ 6 - 2
core/src/main/java/org/springframework/security/provisioning/JdbcUserDetailsManager.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2021 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.GrantedAuthority;
 import org.springframework.security.core.authority.AuthorityUtils;
 import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.security.core.userdetails.UserCache;
@@ -277,7 +278,10 @@ public class JdbcUserDetailsManager extends JdbcDaoImpl implements UserDetailsMa
 		}
 		this.logger.debug("Changing password for user '" + username + "'");
 		getJdbcTemplate().update(this.changePasswordSql, newPassword, username);
-		SecurityContextHolder.getContext().setAuthentication(createNewAuthentication(currentUser, newPassword));
+		Authentication authentication = createNewAuthentication(currentUser, newPassword);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authentication);
+		SecurityContextHolder.setContext(context);
 		this.userCache.removeUserFromCache(username);
 	}
 

+ 4 - 1
remoting/src/main/java/org/springframework/security/remoting/rmi/ContextPropagatingRemoteInvocation.java

@@ -27,6 +27,7 @@ import org.springframework.remoting.support.RemoteInvocation;
 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.SpringSecurityCoreVersion;
+import org.springframework.security.core.context.SecurityContext;
 import org.springframework.security.core.context.SecurityContextHolder;
 
 /**
@@ -97,7 +98,9 @@ public class ContextPropagatingRemoteInvocation extends RemoteInvocation {
 		if (this.principal != null) {
 			Authentication request = createAuthenticationRequest(this.principal, this.credentials);
 			request.setAuthenticated(false);
-			SecurityContextHolder.getContext().setAuthentication(request);
+			SecurityContext context = SecurityContextHolder.createEmptyContext();
+			context.setAuthentication(request);
+			SecurityContextHolder.setContext(context);
 			logger.debug(LogMessage.format("Set SecurityContextHolder to contain: %s", request));
 		}
 		try {

+ 4 - 2
web/src/main/java/org/springframework/security/web/access/ExceptionTranslationFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2004-2020 the original author or authors.
+ * Copyright 2004-2021 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.
@@ -36,6 +36,7 @@ import org.springframework.security.authentication.InsufficientAuthenticationExc
 import org.springframework.security.core.Authentication;
 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.web.AuthenticationEntryPoint;
 import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
@@ -208,7 +209,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
-		SecurityContextHolder.getContext().setAuthentication(null);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		SecurityContextHolder.setContext(context);
 		this.requestCache.saveRequest(request, response);
 		this.authenticationEntryPoint.commence(request, response, reason);
 	}

+ 4 - 1
web/src/main/java/org/springframework/security/web/authentication/AbstractAuthenticationProcessingFilter.java

@@ -38,6 +38,7 @@ import org.springframework.security.authentication.event.InteractiveAuthenticati
 import org.springframework.security.core.Authentication;
 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.web.authentication.session.NullAuthenticatedSessionStrategy;
 import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
@@ -310,7 +311,9 @@ public abstract class AbstractAuthenticationProcessingFilter extends GenericFilt
 	 */
 	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain,
 			Authentication authResult) throws IOException, ServletException {
-		SecurityContextHolder.getContext().setAuthentication(authResult);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authResult);
+		SecurityContextHolder.setContext(context);
 		if (this.logger.isDebugEnabled()) {
 			this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
 		}

+ 5 - 1
web/src/main/java/org/springframework/security/web/authentication/AnonymousAuthenticationFilter.java

@@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationDetailsSource;
 import org.springframework.security.core.Authentication;
 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.util.Assert;
 import org.springframework.web.filter.GenericFilterBean;
@@ -87,7 +88,10 @@ public class AnonymousAuthenticationFilter extends GenericFilterBean implements
 	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
 			throws IOException, ServletException {
 		if (SecurityContextHolder.getContext().getAuthentication() == null) {
-			SecurityContextHolder.getContext().setAuthentication(createAuthentication((HttpServletRequest) req));
+			Authentication authentication = createAuthentication((HttpServletRequest) req);
+			SecurityContext context = SecurityContextHolder.createEmptyContext();
+			context.setAuthentication(authentication);
+			SecurityContextHolder.setContext(context);
 			if (this.logger.isTraceEnabled()) {
 				this.logger.trace(LogMessage.of(() -> "Set SecurityContextHolder to "
 						+ SecurityContextHolder.getContext().getAuthentication()));

+ 4 - 1
web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java

@@ -70,9 +70,12 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
 		}
 		if (this.clearAuthentication) {
 			SecurityContext context = SecurityContextHolder.getContext();
+			SecurityContextHolder.clearContext();
 			context.setAuthentication(null);
 		}
-		SecurityContextHolder.clearContext();
+		else {
+			SecurityContextHolder.clearContext();
+		}
 	}
 
 	public boolean isInvalidateHttpSession() {

+ 5 - 2
web/src/main/java/org/springframework/security/web/authentication/preauth/AbstractPreAuthenticatedProcessingFilter.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2018 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -34,6 +34,7 @@ import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
 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.web.WebAttributes;
 import org.springframework.security.web.authentication.AuthenticationFailureHandler;
@@ -206,7 +207,9 @@ public abstract class AbstractPreAuthenticatedProcessingFilter extends GenericFi
 	protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
 			Authentication authResult) throws IOException, ServletException {
 		this.logger.debug(LogMessage.format("Authentication success: %s", authResult));
-		SecurityContextHolder.getContext().setAuthentication(authResult);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		context.setAuthentication(authResult);
+		SecurityContextHolder.setContext(context);
 		if (this.eventPublisher != null) {
 			this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
 		}

+ 4 - 1
web/src/main/java/org/springframework/security/web/authentication/rememberme/RememberMeAuthenticationFilter.java

@@ -32,6 +32,7 @@ import org.springframework.security.authentication.AuthenticationManager;
 import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
 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.web.authentication.AuthenticationSuccessHandler;
 import org.springframework.security.web.authentication.RememberMeServices;
@@ -107,7 +108,9 @@ public class RememberMeAuthenticationFilter extends GenericFilterBean implements
 			try {
 				rememberMeAuth = this.authenticationManager.authenticate(rememberMeAuth);
 				// Store to SecurityContextHolder
-				SecurityContextHolder.getContext().setAuthentication(rememberMeAuth);
+				SecurityContext context = SecurityContextHolder.createEmptyContext();
+				context.setAuthentication(rememberMeAuth);
+				SecurityContextHolder.setContext(context);
 				onSuccessfulAuthentication(request, response, rememberMeAuth);
 				this.logger.debug(LogMessage.of(() -> "SecurityContextHolder populated with remember-me token: '"
 						+ SecurityContextHolder.getContext().getAuthentication() + "'"));

+ 7 - 2
web/src/main/java/org/springframework/security/web/authentication/switchuser/SwitchUserFilter.java

@@ -47,6 +47,7 @@ import org.springframework.security.core.Authentication;
 import org.springframework.security.core.AuthenticationException;
 import org.springframework.security.core.GrantedAuthority;
 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.userdetails.UserDetails;
 import org.springframework.security.core.userdetails.UserDetailsChecker;
@@ -174,7 +175,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 			try {
 				Authentication targetUser = attemptSwitchUser(request);
 				// update the current context to the new target user
-				SecurityContextHolder.getContext().setAuthentication(targetUser);
+				SecurityContext context = SecurityContextHolder.createEmptyContext();
+				context.setAuthentication(targetUser);
+				SecurityContextHolder.setContext(context);
 				// redirect to target url
 				this.successHandler.onAuthenticationSuccess(request, response, targetUser);
 			}
@@ -188,7 +191,9 @@ public class SwitchUserFilter extends GenericFilterBean implements ApplicationEv
 			// get the original authentication object (if exists)
 			Authentication originalUser = attemptExitUser(request);
 			// update the current context back to the original user
-			SecurityContextHolder.getContext().setAuthentication(originalUser);
+			SecurityContext context = SecurityContextHolder.createEmptyContext();
+			context.setAuthentication(originalUser);
+			SecurityContextHolder.setContext(context);
 			// redirect to target url
 			this.successHandler.onAuthenticationSuccess(request, response, originalUser);
 			return;

+ 4 - 1
web/src/main/java/org/springframework/security/web/authentication/www/BasicAuthenticationFilter.java

@@ -31,6 +31,7 @@ import org.springframework.security.authentication.AuthenticationManager;
 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.web.AuthenticationEntryPoint;
 import org.springframework.security.web.authentication.NullRememberMeServices;
@@ -153,7 +154,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);
-				SecurityContextHolder.getContext().setAuthentication(authResult);
+				SecurityContext context = SecurityContextHolder.createEmptyContext();
+				context.setAuthentication(authResult);
+				SecurityContextHolder.setContext(context);
 				if (this.logger.isDebugEnabled()) {
 					this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
 				}

+ 2 - 1
web/src/main/java/org/springframework/security/web/authentication/www/DigestAuthenticationFilter.java

@@ -210,7 +210,8 @@ public class DigestAuthenticationFilter extends GenericFilterBean implements Mes
 
 	private void fail(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed)
 			throws IOException, ServletException {
-		SecurityContextHolder.getContext().setAuthentication(null);
+		SecurityContext context = SecurityContextHolder.createEmptyContext();
+		SecurityContextHolder.setContext(context);
 		logger.debug(failed);
 		this.authenticationEntryPoint.commence(request, response, failed);
 	}

+ 4 - 2
web/src/main/java/org/springframework/security/web/servletapi/HttpServlet3RequestFactory.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-2021 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.
@@ -225,7 +225,9 @@ final class HttpServlet3RequestFactory implements HttpServletRequestFactory {
 				return;
 			}
 			Authentication authentication = getAuthentication(authManager, username, password);
-			SecurityContextHolder.getContext().setAuthentication(authentication);
+			SecurityContext context = SecurityContextHolder.createEmptyContext();
+			context.setAuthentication(authentication);
+			SecurityContextHolder.setContext(context);
 		}
 
 		private Authentication getAuthentication(AuthenticationManager authManager, String username, String password)