* Creates a new session for the newly authenticated user if they already have a session (as a defence against * session-fixation protection attacks), and copies their @@ -32,7 +32,7 @@ import org.springframework.security.web.savedrequest.SavedRequest; * @version $Id$ * @since 3.0 */ -public class DefaultAuthenticatedSessionStrategy implements AuthenticatedSessionStrategy { +public class DefaultSessionAuthenticationStrategy implements SessionAuthenticationStrategy { protected final Log logger = LogFactory.getLog(this.getClass()); /** diff --git a/web/src/main/java/org/springframework/security/web/session/NullAuthenticatedSessionStrategy.java b/web/src/main/java/org/springframework/security/web/session/NullAuthenticatedSessionStrategy.java index 28d2bd4c81..401488e295 100644 --- a/web/src/main/java/org/springframework/security/web/session/NullAuthenticatedSessionStrategy.java +++ b/web/src/main/java/org/springframework/security/web/session/NullAuthenticatedSessionStrategy.java @@ -11,7 +11,7 @@ import org.springframework.security.core.Authentication; * @version $Id$ * @since 3.0 */ -public final class NullAuthenticatedSessionStrategy implements AuthenticatedSessionStrategy { +public final class NullAuthenticatedSessionStrategy implements SessionAuthenticationStrategy { public void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) { diff --git a/web/src/main/java/org/springframework/security/web/session/SessionAuthenticationException.java b/web/src/main/java/org/springframework/security/web/session/SessionAuthenticationException.java new file mode 100644 index 0000000000..d1b83850de --- /dev/null +++ b/web/src/main/java/org/springframework/security/web/session/SessionAuthenticationException.java @@ -0,0 +1,20 @@ +package org.springframework.security.web.session; + +import org.springframework.security.core.AuthenticationException; + +/** + * Thrown by an SessionAuthenticationStrategy to indicate that an authentication object is not valid for + * the current session, typically because the same user has exceeded the number of sessions they are allowed to have + * concurrently. + * + * @author Luke Taylor + * @version $Id$ + * @since 3.0 + */ +public class SessionAuthenticationException extends AuthenticationException { + + public SessionAuthenticationException(String msg) { + super(msg); + } + +} diff --git a/web/src/main/java/org/springframework/security/web/session/AuthenticatedSessionStrategy.java b/web/src/main/java/org/springframework/security/web/session/SessionAuthenticationStrategy.java similarity index 63% rename from web/src/main/java/org/springframework/security/web/session/AuthenticatedSessionStrategy.java rename to web/src/main/java/org/springframework/security/web/session/SessionAuthenticationStrategy.java index a11f871ec6..aadc3d57c9 100644 --- a/web/src/main/java/org/springframework/security/web/session/AuthenticatedSessionStrategy.java +++ b/web/src/main/java/org/springframework/security/web/session/SessionAuthenticationStrategy.java @@ -7,7 +7,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; /** - * Allows pluggable support for Http session-related behaviour when an authentication occurs. + * Allows pluggable support for HttpSession-related behaviour when an authentication occurs. *
* Typical use would be to make sure a session exists or to change the session Id to guard against session-fixation * attacks. @@ -16,14 +16,15 @@ import org.springframework.security.core.AuthenticationException; * @version $Id$ * @since */ -public interface AuthenticatedSessionStrategy { +public interface SessionAuthenticationStrategy { /** * Performs Http session-related functionality when a new authentication occurs. * - * @throws AuthenticationException if it is decided that the authentication is not allowed for the session. + * @throws SessionAuthenticationException if it is decided that the authentication is not allowed for the session. + * This will typically be because the user has too many sessions open at once. */ void onAuthentication(Authentication authentication, HttpServletRequest request, HttpServletResponse response) - throws AuthenticationException; + throws SessionAuthenticationException; } diff --git a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java index 3cd60d9051..7961fde51c 100644 --- a/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java +++ b/web/src/main/java/org/springframework/security/web/session/SessionManagementFilter.java @@ -21,10 +21,8 @@ import org.springframework.web.filter.GenericFilterBean; /** * Detects that a user has been authenticated since the start of the request and, if they have, calls the - * configured {@link AuthenticatedSessionStrategy} to perform any session-related activity (such as - * activating session-fixation protection mechanisms). - *
- * This is essentially a generalization of the functionality that was implemented for SEC-399. + * configured {@link SessionAuthenticationStrategy} to perform any session-related activity such as + * activating session-fixation protection mechanisms or checking for multiple concurrent logins. * * @author Martin Algesten * @author Luke Taylor @@ -39,7 +37,7 @@ public class SessionManagementFilter extends GenericFilterBean { //~ Instance fields ================================================================================================ private final SecurityContextRepository securityContextRepository; - private AuthenticatedSessionStrategy sessionStrategy = new DefaultAuthenticatedSessionStrategy(); + private SessionAuthenticationStrategy sessionStrategy = new DefaultSessionAuthenticationStrategy(); private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); private String invalidSessionUrl; private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); @@ -65,7 +63,13 @@ public class SessionManagementFilter extends GenericFilterBean { if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) { // The user has been authenticated during the current request, so call the session strategy - sessionStrategy.onAuthentication(authentication, request, response); + try { + sessionStrategy.onAuthentication(authentication, request, response); + } catch (SessionAuthenticationException e) { + // The session strategy can reject the authentication + logger.debug("SessionAuthenticationStrategy rejected the authentication object",e); + SecurityContextHolder.clearContext(); + } } else { // No security context or authentication present. Check for a session timeout if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) { @@ -83,9 +87,9 @@ public class SessionManagementFilter extends GenericFilterBean { * Sets the strategy object which handles the session management behaviour when a * user has been authenticated during the current request. * - * @param sessionStrategy the strategy object. If not set, a {@link DefaultAuthenticatedSessionStrategy} is used. + * @param sessionStrategy the strategy object. If not set, a {@link DefaultSessionAuthenticationStrategy} is used. */ - public void setAuthenticatedSessionStrategy(AuthenticatedSessionStrategy sessionStrategy) { + public void setAuthenticatedSessionStrategy(SessionAuthenticationStrategy sessionStrategy) { Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null"); this.sessionStrategy = sessionStrategy; } diff --git a/web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java b/web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java index 5db5708cf4..a116801aca 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/AbstractProcessingFilterTests.java @@ -50,7 +50,7 @@ import org.springframework.security.web.authentication.SavedRequestAwareAuthenti import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; import org.springframework.security.web.savedrequest.SavedRequest; -import org.springframework.security.web.session.AuthenticatedSessionStrategy; +import org.springframework.security.web.session.SessionAuthenticationStrategy; /** @@ -240,7 +240,7 @@ public class AbstractProcessingFilterTests extends TestCase { MockAbstractProcessingFilter filter = new MockAbstractProcessingFilter(true); filter.setFilterProcessesUrl("/j_mock_post"); - filter.setAuthenticatedSessionStrategy(mock(AuthenticatedSessionStrategy.class)); + filter.setAuthenticatedSessionStrategy(mock(SessionAuthenticationStrategy.class)); filter.setAuthenticationSuccessHandler(successHandler); filter.setAuthenticationFailureHandler(failureHandler); filter.setAuthenticationManager(mock(AuthenticationManager.class)); diff --git a/web/src/test/java/org/springframework/security/web/session/DefaultAuthenticatedSessionStrategyTests.java b/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java similarity index 84% rename from web/src/test/java/org/springframework/security/web/session/DefaultAuthenticatedSessionStrategyTests.java rename to web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java index 5d33f633e0..fe78bb5373 100644 --- a/web/src/test/java/org/springframework/security/web/session/DefaultAuthenticatedSessionStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/session/DefaultSessionAuthenticationStrategyTests.java @@ -18,11 +18,11 @@ import org.springframework.security.web.savedrequest.SavedRequest; * @author Luke Taylor * @version $Id$ */ -public class DefaultAuthenticatedSessionStrategyTests { +public class DefaultSessionAuthenticationStrategyTests { @Test public void newSessionShouldNotBeCreatedIfNoSessionExistsAndAlwaysCreateIsFalse() throws Exception { - DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy(); + DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy(); HttpServletRequest request = new MockHttpServletRequest(); strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse()); @@ -32,7 +32,7 @@ public class DefaultAuthenticatedSessionStrategyTests { // @Test // public void newSessionIsCreatedIfSessionAlreadyExists() throws Exception { -// DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy(); +// DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy(); // strategy.setSessionRegistry(mock(SessionRegistry.class)); // HttpServletRequest request = new MockHttpServletRequest(); // String sessionId = request.getSession().getId(); @@ -45,7 +45,7 @@ public class DefaultAuthenticatedSessionStrategyTests { // See SEC-1077 @Test public void onlySavedRequestAttributeIsMigratedIfMigrateAttributesIsFalse() throws Exception { - DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy(); + DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy(); strategy.setMigrateSessionAttributes(false); HttpServletRequest request = new MockHttpServletRequest(); HttpSession session = request.getSession(); @@ -60,7 +60,7 @@ public class DefaultAuthenticatedSessionStrategyTests { @Test public void sessionIsCreatedIfAlwaysCreateTrue() throws Exception { - DefaultAuthenticatedSessionStrategy strategy = new DefaultAuthenticatedSessionStrategy(); + DefaultSessionAuthenticationStrategy strategy = new DefaultSessionAuthenticationStrategy(); strategy.setAlwaysCreateSession(true); HttpServletRequest request = new MockHttpServletRequest(); strategy.onAuthentication(mock(Authentication.class), request, new MockHttpServletResponse()); diff --git a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java index a8aef52319..e5a3eb9431 100644 --- a/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/session/SessionManagementFilterTests.java @@ -44,7 +44,7 @@ public class SessionManagementFilterTests { @Test public void strategyIsNotInvokedIfSecurityContextAlreadyExistsForRequest() throws Exception { SecurityContextRepository repo = mock(SecurityContextRepository.class); - AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class); + SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class); // mock that repo contains a security context when(repo.containsContext(any(HttpServletRequest.class))).thenReturn(true); SessionManagementFilter filter = new SessionManagementFilter(repo); @@ -60,7 +60,7 @@ public class SessionManagementFilterTests { @Test public void strategyIsNotInvokedIfAuthenticationIsNull() throws Exception { SecurityContextRepository repo = mock(SecurityContextRepository.class); - AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class); + SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class); SessionManagementFilter filter = new SessionManagementFilter(repo); filter.setAuthenticatedSessionStrategy(strategy); HttpServletRequest request = new MockHttpServletRequest(); @@ -74,7 +74,7 @@ public class SessionManagementFilterTests { public void strategyIsInvokedIfUserIsNewlyAuthenticated() throws Exception { SecurityContextRepository repo = mock(SecurityContextRepository.class); // repo will return false to containsContext() - AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class); + SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class); SessionManagementFilter filter = new SessionManagementFilter(repo); filter.setAuthenticatedSessionStrategy(strategy); HttpServletRequest request = new MockHttpServletRequest(); @@ -92,7 +92,7 @@ public class SessionManagementFilterTests { public void responseIsRedirectedToTimeoutUrlIfSetAndSessionIsInvalid() throws Exception { SecurityContextRepository repo = mock(SecurityContextRepository.class); // repo will return false to containsContext() - AuthenticatedSessionStrategy strategy = mock(AuthenticatedSessionStrategy.class); + SessionAuthenticationStrategy strategy = mock(SessionAuthenticationStrategy.class); SessionManagementFilter filter = new SessionManagementFilter(repo); filter.setAuthenticatedSessionStrategy(strategy); MockHttpServletRequest request = new MockHttpServletRequest();