Skip to content

Commit

Permalink
SEC-2025: SecurityContextLogoutHandler removes Authentication from Se…
Browse files Browse the repository at this point in the history
…curityContext

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.
  • Loading branch information
rwinch committed Oct 5, 2012
1 parent f38df99 commit d3339a1
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,34 @@
package org.springframework.security.web.authentication.logout;


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;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
* 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 ========================================================================================================

Expand All @@ -58,6 +64,11 @@ public void logout(HttpServletRequest request, HttpServletResponse response, Aut
}
}

if(clearAuthentication) {
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(null);
}

SecurityContextHolder.clearContext();
}

Expand All @@ -75,4 +86,14 @@ public void setInvalidateHttpSession(boolean invalidateHttpSession) {
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;
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}

0 comments on commit d3339a1

Please sign in to comment.