diff --git a/config/src/test/java/org/springframework/security/SerializationSamples.java b/config/src/test/java/org/springframework/security/SerializationSamples.java
index fe9f75cd50f..9250ea7766a 100644
--- a/config/src/test/java/org/springframework/security/SerializationSamples.java
+++ b/config/src/test/java/org/springframework/security/SerializationSamples.java
@@ -256,7 +256,7 @@ final class SerializationSamples {
generatorByClassName.put(OAuth2AuthorizationExchange.class, (r) -> TestOAuth2AuthorizationExchanges.success());
generatorByClassName.put(OidcUserInfo.class, (r) -> OidcUserInfo.builder().email("email@example.com").build());
generatorByClassName.put(SessionInformation.class,
- (r) -> new SessionInformation(user, r.alphanumeric(4), new Date(1704378933936L)));
+ (r) -> new SessionInformation(user, r.alphanumeric(4), new Date(1704378933936L), new Date()));
generatorByClassName.put(ReactiveSessionInformation.class,
(r) -> new ReactiveSessionInformation(user, r.alphanumeric(4), Instant.ofEpochMilli(1704378933936L)));
generatorByClassName.put(OAuth2AccessToken.class, (r) -> TestOAuth2AccessTokens.scopes("scope"));
diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceSessionManagementTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceSessionManagementTests.java
index 1efd63aab14..0b7dda3124f 100644
--- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceSessionManagementTests.java
+++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/NamespaceSessionManagementTests.java
@@ -111,7 +111,8 @@ public void authenticateWhenUsingInvalidSessionUrlThenMatchesNamespace() throws
public void authenticateWhenUsingExpiredUrlThenMatchesNamespace() throws Exception {
this.spring.register(CustomSessionManagementConfig.class).autowire();
MockHttpSession session = new MockHttpSession();
- SessionInformation sessionInformation = new SessionInformation(new Object(), session.getId(), new Date(0));
+ SessionInformation sessionInformation = new SessionInformation(new Object(), session.getId(), new Date(0),
+ new Date());
sessionInformation.expireNow();
SessionRegistry sessionRegistry = this.spring.getContext().getBean(SessionRegistry.class);
given(sessionRegistry.getSessionInformation(session.getId())).willReturn(sessionInformation);
diff --git a/config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt b/config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt
index 9117ae757a7..1e91c214da6 100644
--- a/config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt
+++ b/config/src/test/kotlin/org/springframework/security/config/annotation/web/session/SessionConcurrencyDslTests.kt
@@ -101,7 +101,7 @@ class SessionConcurrencyDslTests {
mockkObject(ExpiredUrlConfig.SESSION_REGISTRY)
val session = MockHttpSession()
- val sessionInformation = SessionInformation("", session.id, Date(0))
+ val sessionInformation = SessionInformation("", session.id, Date(0), Date())
sessionInformation.expireNow()
every { ExpiredUrlConfig.SESSION_REGISTRY.getSessionInformation(any()) } returns sessionInformation
@@ -141,7 +141,7 @@ class SessionConcurrencyDslTests {
mockkObject(ExpiredSessionStrategyConfig.SESSION_REGISTRY)
val session = MockHttpSession()
- val sessionInformation = SessionInformation("", session.id, Date(0))
+ val sessionInformation = SessionInformation("", session.id, Date(0), Date())
sessionInformation.expireNow()
every { ExpiredSessionStrategyConfig.SESSION_REGISTRY.getSessionInformation(any()) } returns sessionInformation
diff --git a/core/src/main/java/org/springframework/security/core/session/SessionInformation.java b/core/src/main/java/org/springframework/security/core/session/SessionInformation.java
index 54b05bbbb08..1fba14b0624 100644
--- a/core/src/main/java/org/springframework/security/core/session/SessionInformation.java
+++ b/core/src/main/java/org/springframework/security/core/session/SessionInformation.java
@@ -36,6 +36,7 @@
* Filter
.
*
* @author Ben Alex
+ * @author Andrey Litvitski
*/
public class SessionInformation implements Serializable {
@@ -49,13 +50,17 @@ public class SessionInformation implements Serializable {
private boolean expired = false;
- public SessionInformation(Object principal, String sessionId, Date lastRequest) {
+ private final Date createdTime;
+
+ public SessionInformation(Object principal, String sessionId, Date lastRequest, Date createdTime) {
Assert.notNull(principal, "Principal required");
Assert.hasText(sessionId, "SessionId required");
Assert.notNull(lastRequest, "LastRequest required");
+ Assert.notNull(lastRequest, "CreatedTime required");
this.principal = principal;
this.sessionId = sessionId;
this.lastRequest = lastRequest;
+ this.createdTime = createdTime;
}
public void expireNow() {
@@ -74,6 +79,10 @@ public String getSessionId() {
return this.sessionId;
}
+ public Date getCreatedTime() {
+ return this.createdTime;
+ }
+
public boolean isExpired() {
return this.expired;
}
diff --git a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java
index a3909315415..36fcfefca18 100644
--- a/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java
+++ b/core/src/main/java/org/springframework/security/core/session/SessionRegistryImpl.java
@@ -133,7 +133,9 @@ public void registerNewSession(String sessionId, Object principal) {
if (this.logger.isDebugEnabled()) {
this.logger.debug(LogMessage.format("Registering session %s, for principal %s", sessionId, principal));
}
- this.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date()));
+ Date currentDate = new Date();
+ this.sessionIds.put(sessionId, new SessionInformation(principal, sessionId, new Date(currentDate.getTime()),
+ new Date(currentDate.getTime())));
this.principals.compute(principal, (key, sessionsUsedByPrincipal) -> {
if (sessionsUsedByPrincipal == null) {
sessionsUsedByPrincipal = new CopyOnWriteArraySet<>();
diff --git a/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java b/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java
index e66335017f1..eb0c3e135a6 100644
--- a/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java
+++ b/core/src/test/java/org/springframework/security/core/session/SessionInformationTests.java
@@ -26,6 +26,7 @@
* Tests {@link SessionInformation}.
*
* @author Ben Alex
+ * @author Andrey Litvitski
*/
public class SessionInformationTests {
@@ -34,10 +35,12 @@ public void testObject() throws Exception {
Object principal = "Some principal object";
String sessionId = "1234567890";
Date currentDate = new Date();
- SessionInformation info = new SessionInformation(principal, sessionId, currentDate);
+ SessionInformation info = new SessionInformation(principal, sessionId, new Date(currentDate.getTime()),
+ new Date(currentDate.getTime()));
assertThat(info.getPrincipal()).isEqualTo(principal);
assertThat(info.getSessionId()).isEqualTo(sessionId);
assertThat(info.getLastRequest()).isEqualTo(currentDate);
+ assertThat(info.getCreatedTime()).isEqualTo(currentDate);
Thread.sleep(10);
info.refreshLastRequest();
assertThat(info.getLastRequest().after(currentDate)).isTrue();
diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java
index 693a06d3aa4..8ebc3d7044a 100644
--- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java
+++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/oidc/session/OidcSessionInformation.java
@@ -46,7 +46,11 @@ public class OidcSessionInformation extends SessionInformation {
* @param user the OIDC Provider's session and end user
*/
public OidcSessionInformation(String sessionId, Map authorities, OidcUser user) {
- super(user, sessionId, new Date());
+ this(sessionId, authorities, user, new Date());
+ }
+
+ private OidcSessionInformation(String sessionId, Map authorities, OidcUser user, Date now) {
+ super(user, sessionId, new Date(now.getTime()), new Date(now.getTime()));
this.authorities = (authorities != null) ? new LinkedHashMap<>(authorities) : Collections.emptyMap();
}
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 aa1bed6d8f8..faf9beba109 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
@@ -70,7 +70,7 @@ public void setup() {
this.request = new MockHttpServletRequest();
this.response = new MockHttpServletResponse();
this.sessionInformation = new SessionInformation(this.authentication.getPrincipal(), "unique",
- new Date(1374766134216L));
+ new Date(1374766134216L), new Date());
this.strategy = new ConcurrentSessionControlAuthenticationStrategy(this.sessionRegistry);
}
@@ -123,7 +123,7 @@ public void maxSessionsExpireExistingUser() {
@Test
public void maxSessionsExpireLeastRecentExistingUser() {
SessionInformation moreRecentSessionInfo = new SessionInformation(this.authentication.getPrincipal(), "unique",
- new Date(1374766999999L));
+ new Date(1374766999999L), new Date());
given(this.sessionRegistry.getAllSessions(any(), anyBoolean()))
.willReturn(Arrays.asList(moreRecentSessionInfo, this.sessionInformation));
this.strategy.setMaximumSessions(2);
@@ -134,9 +134,9 @@ public void maxSessionsExpireLeastRecentExistingUser() {
@Test
public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpired() {
SessionInformation oldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(), "unique1",
- new Date(1374766134214L));
+ new Date(1374766134214L), new Date());
SessionInformation secondOldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(),
- "unique2", new Date(1374766134215L));
+ "unique2", new Date(1374766134215L), new Date());
given(this.sessionRegistry.getAllSessions(any(), anyBoolean())).willReturn(
Arrays.asList(oldestSessionInfo, secondOldestSessionInfo, this.sessionInformation));
this.strategy.setMaximumSessions(2);
@@ -196,7 +196,7 @@ public void maxSessionsExpireExistingUserUsingSessionLimit() {
@Test
public void maxSessionsExpireLeastRecentExistingUserUsingSessionLimit() {
SessionInformation moreRecentSessionInfo = new SessionInformation(this.authentication.getPrincipal(), "unique",
- new Date(1374766999999L));
+ new Date(1374766999999L), new Date());
given(this.sessionRegistry.getAllSessions(any(), anyBoolean()))
.willReturn(Arrays.asList(moreRecentSessionInfo, this.sessionInformation));
this.strategy.setMaximumSessions(SessionLimit.of(2));
@@ -207,9 +207,9 @@ public void maxSessionsExpireLeastRecentExistingUserUsingSessionLimit() {
@Test
public void onAuthenticationWhenMaxSessionsExceededByTwoThenTwoSessionsExpiredUsingSessionLimit() {
SessionInformation oldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(), "unique1",
- new Date(1374766134214L));
+ new Date(1374766134214L), new Date());
SessionInformation secondOldestSessionInfo = new SessionInformation(this.authentication.getPrincipal(),
- "unique2", new Date(1374766134215L));
+ "unique2", new Date(1374766134215L), new Date());
given(this.sessionRegistry.getAllSessions(any(), anyBoolean()))
.willReturn(Arrays.asList(oldestSessionInfo, secondOldestSessionInfo, this.sessionInformation));
this.strategy.setMaximumSessions(SessionLimit.of(2));
diff --git a/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java b/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java
index e0c6769fd80..3a424342e43 100644
--- a/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java
+++ b/web/src/test/java/org/springframework/security/web/concurrent/ConcurrentSessionFilterTests.java
@@ -276,7 +276,7 @@ public void setLogoutHandlersWhenEmptyThenThrowsException() {
private SessionRegistry mockSessionRegistry() {
SessionRegistry registry = mock(SessionRegistry.class);
SessionInformation information = new SessionInformation("user", "sessionId",
- new Date(System.currentTimeMillis() - 1000));
+ new Date(System.currentTimeMillis() - 1000), new Date());
information.expireNow();
given(registry.getSessionInformation(anyString())).willReturn(information);
return registry;
diff --git a/web/src/test/java/org/springframework/security/web/session/SessionInformationExpiredEventTests.java b/web/src/test/java/org/springframework/security/web/session/SessionInformationExpiredEventTests.java
index 99d0cbef939..b00976da7ab 100644
--- a/web/src/test/java/org/springframework/security/web/session/SessionInformationExpiredEventTests.java
+++ b/web/src/test/java/org/springframework/security/web/session/SessionInformationExpiredEventTests.java
@@ -43,20 +43,22 @@ public void constructorWhenSessionInformationNullThenThrowsException() {
@Test
public void constructorWhenRequestNullThenThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new SessionInformationExpiredEvent(
- new SessionInformation("fake", "sessionId", new Date()), null, new MockHttpServletResponse()));
+ new SessionInformation("fake", "sessionId", new Date(), new Date()), null,
+ new MockHttpServletResponse()));
}
@Test
public void constructorWhenResponseNullThenThrowsException() {
assertThatIllegalArgumentException().isThrownBy(() -> new SessionInformationExpiredEvent(
- new SessionInformation("fake", "sessionId", new Date()), new MockHttpServletRequest(), null));
+ new SessionInformation("fake", "sessionId", new Date(), new Date()), new MockHttpServletRequest(),
+ null));
}
@Test
void constructorWhenFilterChainThenGetFilterChainReturnsNotNull() {
MockFilterChain filterChain = new MockFilterChain();
SessionInformationExpiredEvent event = new SessionInformationExpiredEvent(
- new SessionInformation("fake", "sessionId", new Date()), new MockHttpServletRequest(),
+ new SessionInformation("fake", "sessionId", new Date(), new Date()), new MockHttpServletRequest(),
new MockHttpServletResponse(), filterChain);
assertThat(event.getFilterChain()).isSameAs(filterChain);
}