Immutable SecurityContext

Issue gh-10032
This commit is contained in:
Josh Cummings 2021-07-28 12:44:46 -06:00
parent 3ab6bee856
commit b8d51725c7
15 changed files with 66 additions and 22 deletions

View File

@ -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()));
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);
}

View File

@ -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 {

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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()));

View File

@ -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() {

View File

@ -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()));
}

View File

@ -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() + "'"));

View File

@ -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;

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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)