SEC-2025: SecurityContextLogoutHandler removes Authentication from SecurityContext

Previously there was a race condition could occur when the user attempts to access
a slow resource and then logs out which would result in the user not being logged
out.

SecurityContextLogoutHandler will now remove the Authentication from the
SecurityContext to protect against this scenario.
This commit is contained in:
Rob Winch 2012-10-05 14:18:19 -05:00
parent f38df99730
commit d3339a1e32
2 changed files with 100 additions and 6 deletions

View File

@ -16,28 +16,34 @@
package org.springframework.security.web.authentication.logout;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
/**
* Performs a logout by modifying the {@link org.springframework.security.core.context.SecurityContextHolder}.
* <p>
* Will also invalidate the {@link HttpSession} if {@link #isInvalidateHttpSession()} is {@code true} and the
* session is not {@code null}.
* <p>
* Will also remove the {@link Authentication} from the current {@link SecurityContext} if {@link #clearAuthentication}
* is set to true (default).
*
* @author Ben Alex
* @author Rob Winch
*/
public class SecurityContextLogoutHandler implements LogoutHandler {
protected final Log logger = LogFactory.getLog(this.getClass());
private boolean invalidateHttpSession = true;
private boolean clearAuthentication = true;
//~ Methods ========================================================================================================
@ -58,6 +64,11 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
}
}
if(clearAuthentication) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(null);
}
SecurityContextHolder.clearContext();
}
@ -75,4 +86,14 @@ public class SecurityContextLogoutHandler implements LogoutHandler {
this.invalidateHttpSession = invalidateHttpSession;
}
/**
* If true, removes the {@link Authentication} from the {@link SecurityContext} to prevent issues with concurrent
* requests.
*
* @param clearAuthentication true if you wish to clear the {@link Authentication} from the {@link SecurityContext}
* (default) or false if the {@link Authentication} should not be removed.
*/
public void setClearAuthentication(boolean clearAuthentication) {
this.clearAuthentication = clearAuthentication;
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2002-2012 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. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.security.web.authentication.logout;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
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.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
/**
*
* @author Rob Winch
*
*/
public class SecurityContextLogoutHandlerTests {
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private SecurityContextLogoutHandler handler;
@Before
public void setUp() {
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
handler = new SecurityContextLogoutHandler();
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(new TestingAuthenticationToken("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER")));
SecurityContextHolder.setContext(context);
}
@After
public void tearDown() {
SecurityContextHolder.clearContext();
}
// SEC-2025
@Test
public void clearsAuthentication() {
SecurityContext beforeContext = SecurityContextHolder.getContext();
handler.logout(request, response, SecurityContextHolder.getContext().getAuthentication());
assertNull(beforeContext.getAuthentication());
}
@Test
public void disableClearsAuthentication() {
handler.setClearAuthentication(false);
SecurityContext beforeContext = SecurityContextHolder.getContext();
Authentication beforeAuthentication = beforeContext.getAuthentication();
handler.logout(request, response, SecurityContextHolder.getContext().getAuthentication());
assertNotNull(beforeContext.getAuthentication());
assertSame(beforeAuthentication, beforeContext.getAuthentication());
}
}