diff --git a/web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java b/web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java index 512a65fe40..a5c90b521b 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java +++ b/web/src/main/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandler.java @@ -16,24 +16,30 @@ package org.springframework.security.web.authentication.logout; -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.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}. *
* Will also invalidate the {@link HttpSession} if {@link #isInvalidateHttpSession()} is true
and the
* session is not null
.
+ *
+ * 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 { private boolean invalidateHttpSession = true; + private boolean clearAuthentication = true; //~ Methods ======================================================================================================== @@ -53,6 +59,11 @@ public class SecurityContextLogoutHandler implements LogoutHandler { } } + if(clearAuthentication) { + SecurityContext context = SecurityContextHolder.getContext(); + context.setAuthentication(null); + } + SecurityContextHolder.clearContext(); } @@ -70,4 +81,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; + } } diff --git a/web/src/test/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandlerTests.java b/web/src/test/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandlerTests.java new file mode 100644 index 0000000000..be61a4686f --- /dev/null +++ b/web/src/test/java/org/springframework/security/web/authentication/logout/SecurityContextLogoutHandlerTests.java @@ -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()); + } +}