From a17b75e862e3dac4ecf9ae3798ed0e2e74cbac0a Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Tue, 13 Aug 2019 17:09:04 -0400 Subject: [PATCH] Expire as many sessions as exceed maximum allowed Fixes: gh-7166 --- ...tSessionControlAuthenticationStrategy.java | 25 ++++++++----------- ...ionControlAuthenticationStrategyTests.java | 21 +++++++++++++++- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java index 68df38f902..2e7401207c 100644 --- a/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java +++ b/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2019 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. @@ -15,6 +15,7 @@ */ package org.springframework.security.web.authentication.session; +import java.util.Comparator; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -45,8 +46,9 @@ import org.springframework.util.Assert; *

*

* If a user has reached the maximum number of permitted sessions, the behaviour depends - * on the exceptionIfMaxExceeded property. The default behaviour is to expired - * the least recently used session, which will be invalidated by the + * on the exceptionIfMaxExceeded property. The default behaviour is to expire + * any sessions that exceed the maximum number of permitted sessions, starting with the + * least recently used sessions. The expired sessions will be invalidated by the * {@link ConcurrentSessionFilter} if accessed again. If exceptionIfMaxExceeded * is set to true, however, the user will be prevented from starting a new * authenticated session. @@ -156,18 +158,13 @@ public class ConcurrentSessionControlAuthenticationStrategy implements "Maximum sessions of {0} for this principal exceeded")); } - // Determine least recently used session, and mark it for invalidation - SessionInformation leastRecentlyUsed = null; - - for (SessionInformation session : sessions) { - if ((leastRecentlyUsed == null) - || session.getLastRequest() - .before(leastRecentlyUsed.getLastRequest())) { - leastRecentlyUsed = session; - } + // Determine least recently used sessions, and mark them for invalidation + sessions.sort(Comparator.comparing(SessionInformation::getLastRequest)); + int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1; + List sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy); + for (SessionInformation session: sessionsToBeExpired) { + session.expireNow(); } - - leastRecentlyUsed.expireNow(); } /** diff --git a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java index 1082868356..409f11af78 100644 --- a/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2019 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. @@ -134,6 +134,25 @@ public class ConcurrentSessionControlAuthenticationStrategyTests { assertThat(sessionInformation.isExpired()).isTrue(); } + @Test + public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() { + SessionInformation oldestSessionInfo = new SessionInformation( + authentication.getPrincipal(), "unique1", new Date(1374766134214L)); + SessionInformation secondOldestSessionInfo = new SessionInformation( + authentication.getPrincipal(), "unique2", new Date(1374766134215L)); + when(sessionRegistry.getAllSessions(any(), anyBoolean())).thenReturn( + Arrays. asList(oldestSessionInfo, + secondOldestSessionInfo, + sessionInformation)); + strategy.setMaximumSessions(2); + + strategy.onAuthentication(authentication, request, response); + + assertThat(oldestSessionInfo.isExpired()).isTrue(); + assertThat(secondOldestSessionInfo.isExpired()).isTrue(); + assertThat(sessionInformation.isExpired()).isFalse(); + } + @Test(expected = IllegalArgumentException.class) public void setMessageSourceNull() { strategy.setMessageSource(null);