Skip to content

Commit 43ad2e1

Browse files
committed
Expire as many sessions as exceed maximum allowed
Fixes: gh-7166
1 parent ee9a3a2 commit 43ad2e1

File tree

2 files changed

+31
-15
lines changed

2 files changed

+31
-15
lines changed

web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.security.web.authentication.session;
1717

18+
import java.util.Comparator;
1819
import java.util.List;
1920

2021
import javax.servlet.http.HttpServletRequest;
@@ -45,8 +46,9 @@
4546
* </p>
4647
* <p>
4748
* If a user has reached the maximum number of permitted sessions, the behaviour depends
48-
* on the <tt>exceptionIfMaxExceeded</tt> property. The default behaviour is to expired
49-
* the least recently used session, which will be invalidated by the
49+
* on the <tt>exceptionIfMaxExceeded</tt> property. The default behaviour is to expire
50+
* any sessions that exceed the maximum number of permitted sessions, starting with the
51+
* least recently used sessions. The expired sessions will be invalidated by the
5052
* {@link ConcurrentSessionFilter} if accessed again. If <tt>exceptionIfMaxExceeded</tt>
5153
* is set to <tt>true</tt>, however, the user will be prevented from starting a new
5254
* authenticated session.
@@ -156,18 +158,13 @@ protected void allowableSessionsExceeded(List<SessionInformation> sessions,
156158
"Maximum sessions of {0} for this principal exceeded"));
157159
}
158160

159-
// Determine least recently used session, and mark it for invalidation
160-
SessionInformation leastRecentlyUsed = null;
161-
162-
for (SessionInformation session : sessions) {
163-
if ((leastRecentlyUsed == null)
164-
|| session.getLastRequest()
165-
.before(leastRecentlyUsed.getLastRequest())) {
166-
leastRecentlyUsed = session;
167-
}
161+
// Determine least recently used sessions, and mark them for invalidation
162+
sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
163+
int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
164+
List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
165+
for (SessionInformation session: sessionsToBeExpired) {
166+
session.expireNow();
168167
}
169-
170-
leastRecentlyUsed.expireNow();
171168
}
172169

173170
/**

web/src/test/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategyTests.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -134,6 +134,25 @@ Arrays.<SessionInformation> asList(moreRecentSessionInfo,
134134
assertThat(sessionInformation.isExpired()).isTrue();
135135
}
136136

137+
@Test
138+
public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() {
139+
SessionInformation oldestSessionInfo = new SessionInformation(
140+
authentication.getPrincipal(), "unique1", new Date(1374766134214L));
141+
SessionInformation secondOldestSessionInfo = new SessionInformation(
142+
authentication.getPrincipal(), "unique2", new Date(1374766134215L));
143+
when(sessionRegistry.getAllSessions(any(), anyBoolean())).thenReturn(
144+
Arrays.<SessionInformation> asList(oldestSessionInfo,
145+
secondOldestSessionInfo,
146+
sessionInformation));
147+
strategy.setMaximumSessions(2);
148+
149+
strategy.onAuthentication(authentication, request, response);
150+
151+
assertThat(oldestSessionInfo.isExpired()).isTrue();
152+
assertThat(secondOldestSessionInfo.isExpired()).isTrue();
153+
assertThat(sessionInformation.isExpired()).isFalse();
154+
}
155+
137156
@Test(expected = IllegalArgumentException.class)
138157
public void setMessageSourceNull() {
139158
strategy.setMessageSource(null);

0 commit comments

Comments
 (0)