diff --git a/spring-session-core/src/main/java/org/springframework/session/MapSession.java b/spring-session-core/src/main/java/org/springframework/session/MapSession.java index 485883f1b..f38adff3a 100644 --- a/spring-session-core/src/main/java/org/springframework/session/MapSession.java +++ b/spring-session-core/src/main/java/org/springframework/session/MapSession.java @@ -23,7 +23,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.UUID; /** *

@@ -44,6 +43,7 @@ * * @author Rob Winch * @author Vedran Pavic + * @author Jakub Maciej * @since 1.0 */ public final class MapSession implements Session, Serializable { @@ -68,13 +68,6 @@ public final class MapSession implements Session, Serializable { */ private Duration maxInactiveInterval = Duration.ofSeconds(DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS); - /** - * Creates a new instance with a secure randomly generated identifier. - */ - public MapSession() { - this(generateId()); - } - /** * Creates a new instance with the specified id. This is preferred to the default * constructor when the id is known to prevent unnecessary consumption on entropy @@ -124,22 +117,20 @@ public String getId() { return this.id; } + @Override + public void changeSessionId(final String id) { + setId(id); + } + /** * Get the original session id. * @return the original session id - * @see #changeSessionId() + * @see #changeSessionId(String changedId) */ public String getOriginalId() { return this.originalId; } - @Override - public String changeSessionId() { - String changedId = generateId(); - setId(changedId); - return changedId; - } - @Override public Instant getLastAccessedTime() { return this.lastAccessedTime; @@ -222,10 +213,6 @@ public int hashCode() { return this.id.hashCode(); } - private static String generateId() { - return UUID.randomUUID().toString(); - } - private static final long serialVersionUID = 7160779239673823561L; } diff --git a/spring-session-core/src/main/java/org/springframework/session/MapSessionRepository.java b/spring-session-core/src/main/java/org/springframework/session/MapSessionRepository.java index 373c01824..44858a9c4 100644 --- a/spring-session-core/src/main/java/org/springframework/session/MapSessionRepository.java +++ b/spring-session-core/src/main/java/org/springframework/session/MapSessionRepository.java @@ -21,6 +21,7 @@ import org.springframework.session.events.SessionDeletedEvent; import org.springframework.session.events.SessionExpiredEvent; +import org.springframework.util.Assert; /** * A {@link SessionRepository} backed by a {@link java.util.Map} and that uses a @@ -34,6 +35,7 @@ *

* * @author Rob Winch + * @author Jakub Maciej * @since 1.0 */ public class MapSessionRepository implements SessionRepository { @@ -46,6 +48,8 @@ public class MapSessionRepository implements SessionRepository { private final Map sessions; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + /** * Creates a new instance backed by the provided {@link java.util.Map}. This allows * injecting a distributed {@link java.util.Map}. @@ -94,13 +98,30 @@ public void deleteById(String id) { this.sessions.remove(id); } + @Override + public String changeSessionId(final MapSession session) { + String newId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newId); + return newId; + } + @Override public MapSession createSession() { - MapSession result = new MapSession(); + MapSession result = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } return result; } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + } diff --git a/spring-session-core/src/main/java/org/springframework/session/ReactiveMapSessionRepository.java b/spring-session-core/src/main/java/org/springframework/session/ReactiveMapSessionRepository.java index ed3d843fd..a01ddf3cf 100644 --- a/spring-session-core/src/main/java/org/springframework/session/ReactiveMapSessionRepository.java +++ b/spring-session-core/src/main/java/org/springframework/session/ReactiveMapSessionRepository.java @@ -23,6 +23,7 @@ import org.springframework.session.events.SessionDeletedEvent; import org.springframework.session.events.SessionExpiredEvent; +import org.springframework.util.Assert; /** * A {@link ReactiveSessionRepository} backed by a {@link Map} and that uses a @@ -36,10 +37,13 @@ *

* * @author Rob Winch + * @author Jakub Maciej * @since 2.0 */ public class ReactiveMapSessionRepository implements ReactiveSessionRepository { + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + /** * If non-null, this value is used to override * {@link Session#setMaxInactiveInterval(Duration)}. @@ -98,7 +102,7 @@ public Mono deleteById(String id) { @Override public Mono createSession() { return Mono.defer(() -> { - MapSession result = new MapSession(); + MapSession result = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { result.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } @@ -106,4 +110,14 @@ public Mono createSession() { }); } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + } diff --git a/spring-session-core/src/main/java/org/springframework/session/Session.java b/spring-session-core/src/main/java/org/springframework/session/Session.java index 6be3445cc..34683832e 100644 --- a/spring-session-core/src/main/java/org/springframework/session/Session.java +++ b/spring-session-core/src/main/java/org/springframework/session/Session.java @@ -26,6 +26,7 @@ * * @author Rob Winch * @author Vedran Pavic + * @author Jakub Maciej * @since 1.0 */ public interface Session { @@ -39,9 +40,9 @@ public interface Session { /** * Changes the session id. After invoking the {@link #getId()} will return a new * identifier. - * @return the new session id which {@link #getId()} will now return + * @param id desired new sessionId */ - String changeSessionId(); + void changeSessionId(String id); /** * Gets the Object associated with the specified name or null if no Object is diff --git a/spring-session-core/src/main/java/org/springframework/session/SessionIdStrategy.java b/spring-session-core/src/main/java/org/springframework/session/SessionIdStrategy.java new file mode 100644 index 000000000..f7b00c1ad --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/SessionIdStrategy.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014-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. + * You may obtain a copy of the License at + * + * https://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.session; + +/** + * Provides a common interface for id generation strategy. Allows customization of session + * id creation process. + * + * @author Jakub Maciej + */ +public interface SessionIdStrategy { + + /** + * Gets a unique string that identifies the {@link Session}. + * @return a unique string that identifies the {@link Session} + */ + String createSessionId(); + + /** + * Gets default strategy used to generate session id {@link SessionIdStrategy}. + * @return gets default strategy used to generate session id {@link SessionIdStrategy} + */ + static SessionIdStrategy getDefault() { + return new UUIDSessionIdStrategy(); + } + +} diff --git a/spring-session-core/src/main/java/org/springframework/session/SessionRepository.java b/spring-session-core/src/main/java/org/springframework/session/SessionRepository.java index 7efe2f995..bbc79d821 100644 --- a/spring-session-core/src/main/java/org/springframework/session/SessionRepository.java +++ b/spring-session-core/src/main/java/org/springframework/session/SessionRepository.java @@ -21,6 +21,7 @@ * * @param the {@link Session} type * @author Rob Winch + * @author Jakub Maciej * @since 1.0 */ public interface SessionRepository { @@ -68,4 +69,11 @@ public interface SessionRepository { */ void deleteById(String id); + /** + * Changes the session id. + * @param session which id should be changed + * @return new session id + */ + String changeSessionId(S session); + } diff --git a/spring-session-core/src/main/java/org/springframework/session/UUIDSessionIdStrategy.java b/spring-session-core/src/main/java/org/springframework/session/UUIDSessionIdStrategy.java new file mode 100644 index 000000000..3a16e766a --- /dev/null +++ b/spring-session-core/src/main/java/org/springframework/session/UUIDSessionIdStrategy.java @@ -0,0 +1,34 @@ +/* + * Copyright 2014-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. + * You may obtain a copy of the License at + * + * https://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.session; + +import java.util.UUID; + +/** + * UUID based session generation strategy. This is default strategy for session id + * generation. + * + * @author Jakub Maciej + */ +public class UUIDSessionIdStrategy implements SessionIdStrategy { + + @Override + public String createSessionId() { + return UUID.randomUUID().toString(); + } + +} diff --git a/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java b/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java index c12e80081..b5e91b6b8 100644 --- a/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java +++ b/spring-session-core/src/main/java/org/springframework/session/web/http/SessionRepositoryFilter.java @@ -75,6 +75,7 @@ * @author Rob Winch * @author Vedran Pavic * @author Josh Cummings + * @author Jakub Maciej */ @Order(SessionRepositoryFilter.DEFAULT_ORDER) public class SessionRepositoryFilter extends OncePerRequestFilter { @@ -254,7 +255,7 @@ public String changeSessionId() { "Cannot change session ID. There is no session associated with this request."); } - return getCurrentSession().getSession().changeSessionId(); + return SessionRepositoryFilter.this.sessionRepository.changeSessionId(getCurrentSession().getSession()); } @Override diff --git a/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java b/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java index 97e0d9103..ab472e61e 100644 --- a/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java +++ b/spring-session-core/src/main/java/org/springframework/session/web/server/session/SpringSessionWebSessionStore.java @@ -35,6 +35,7 @@ import org.springframework.lang.Nullable; import org.springframework.session.ReactiveSessionRepository; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.util.Assert; import org.springframework.web.server.WebSession; import org.springframework.web.server.session.WebSessionStore; @@ -47,12 +48,15 @@ * @param the {@link Session} type * @author Rob Winch * @author Vedran Pavic + * @author Jakub Maciej * @since 2.0 */ public class SpringSessionWebSessionStore implements WebSessionStore { private final ReactiveSessionRepository sessions; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + private Clock clock = Clock.system(ZoneOffset.UTC); public SpringSessionWebSessionStore(ReactiveSessionRepository reactiveSessionRepository) { @@ -108,6 +112,16 @@ private SpringSessionWebSession existingSession(S session) { return new SpringSessionWebSession(session, State.STARTED); } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + /** * Adapts Spring Session's {@link Session} to a {@link WebSession}. */ @@ -134,7 +148,7 @@ public String getId() { @Override public Mono changeSessionId() { return Mono.defer(() -> { - this.session.changeSessionId(); + this.session.changeSessionId(SpringSessionWebSessionStore.this.sessionIdStrategy.createSessionId()); return save(); }); } diff --git a/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java b/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java index f5e243391..430ecaeba 100644 --- a/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/DelegatingIndexResolverTests.java @@ -40,7 +40,7 @@ void setUp() { @Test void resolve() { - MapSession session = new MapSession(); + MapSession session = new MapSession("1"); session.setAttribute("one", "first"); session.setAttribute("two", "second"); Map indexes = this.indexResolver.resolveIndexesFor(session); diff --git a/spring-session-core/src/test/java/org/springframework/session/MapSessionRepositoryTests.java b/spring-session-core/src/test/java/org/springframework/session/MapSessionRepositoryTests.java index ede43f20d..b25b7c4fa 100644 --- a/spring-session-core/src/test/java/org/springframework/session/MapSessionRepositoryTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/MapSessionRepositoryTests.java @@ -38,7 +38,7 @@ class MapSessionRepositoryTests { @BeforeEach void setup() { this.repository = new MapSessionRepository(new ConcurrentHashMap<>()); - this.session = new MapSession(); + this.session = new MapSession("1"); } @Test @@ -55,12 +55,12 @@ void createSessionDefaultExpiration() { Session session = this.repository.createSession(); assertThat(session).isInstanceOf(MapSession.class); - assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession("1").getMaxInactiveInterval()); } @Test void createSessionCustomDefaultExpiration() { - final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10); + final Duration expectedMaxInterval = new MapSession("1").getMaxInactiveInterval().plusSeconds(10); this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds()); Session session = this.repository.createSession(); @@ -73,7 +73,7 @@ void changeSessionIdWhenNotYetSaved() { MapSession createSession = this.repository.createSession(); String originalId = createSession.getId(); - createSession.changeSessionId(); + createSession.changeSessionId("1"); this.repository.save(createSession); @@ -88,7 +88,7 @@ void changeSessionIdWhenSaved() { this.repository.save(createSession); String originalId = createSession.getId(); - createSession.changeSessionId(); + createSession.changeSessionId("1"); this.repository.save(createSession); diff --git a/spring-session-core/src/test/java/org/springframework/session/MapSessionTests.java b/spring-session-core/src/test/java/org/springframework/session/MapSessionTests.java index 36221f5e9..ea944b3fd 100644 --- a/spring-session-core/src/test/java/org/springframework/session/MapSessionTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/MapSessionTests.java @@ -32,7 +32,7 @@ class MapSessionTests { @BeforeEach void setup() { - this.session = new MapSession(); + this.session = new MapSession("1"); this.session.setLastAccessedTime(Instant.ofEpochMilli(1413258262962L)); } @@ -151,7 +151,7 @@ public Instant getCreationTime() { } @Override - public String changeSessionId() { + public void changeSessionId(String id) { throw new UnsupportedOperationException(); } diff --git a/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java b/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java index aec43d1c6..d469d73ee 100644 --- a/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/PrincipalNameIndexResolverTests.java @@ -47,7 +47,7 @@ void setUp() { @Test void resolveFromPrincipalName() { - MapSession session = new MapSession(); + MapSession session = new MapSession("1"); session.setAttribute(FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME, PRINCIPAL_NAME); assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME); } @@ -58,7 +58,7 @@ void resolveFromSpringSecurityContext() { AuthorityUtils.createAuthorityList("ROLE_USER")); SecurityContext context = new SecurityContextImpl(); context.setAuthentication(authentication); - MapSession session = new MapSession(); + MapSession session = new MapSession("1"); session.setAttribute(SPRING_SECURITY_CONTEXT, context); assertThat(this.indexResolver.resolveIndexValueFor(session)).isEqualTo(PRINCIPAL_NAME); } diff --git a/spring-session-core/src/test/java/org/springframework/session/ReactiveMapSessionRepositoryTests.java b/spring-session-core/src/test/java/org/springframework/session/ReactiveMapSessionRepositoryTests.java index 23044fe2e..32bb952d4 100644 --- a/spring-session-core/src/test/java/org/springframework/session/ReactiveMapSessionRepositoryTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/ReactiveMapSessionRepositoryTests.java @@ -98,12 +98,12 @@ void createSessionWhenDefaultMaxInactiveIntervalThenDefaultMaxInactiveInterval() Session session = this.repository.createSession().block(); assertThat(session).isInstanceOf(MapSession.class); - assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession("1").getMaxInactiveInterval()); } @Test void createSessionWhenCustomMaxInactiveIntervalThenCustomMaxInactiveInterval() { - final Duration expectedMaxInterval = new MapSession().getMaxInactiveInterval().plusSeconds(10); + final Duration expectedMaxInterval = new MapSession("1").getMaxInactiveInterval().plusSeconds(10); this.repository.setDefaultMaxInactiveInterval((int) expectedMaxInterval.getSeconds()); Session session = this.repository.createSession().block(); @@ -116,7 +116,7 @@ void changeSessionIdWhenNotYetSaved() { MapSession createSession = this.repository.createSession().block(); String originalId = createSession.getId(); - createSession.changeSessionId(); + createSession.changeSessionId("1"); this.repository.save(createSession).block(); @@ -131,7 +131,7 @@ void changeSessionIdWhenSaved() { this.repository.save(createSession).block(); String originalId = createSession.getId(); - createSession.changeSessionId(); + createSession.changeSessionId("1"); this.repository.save(createSession).block(); diff --git a/spring-session-core/src/test/java/org/springframework/session/config/annotation/web/http/EnableSpringHttpSessionCustomCookieSerializerTests.java b/spring-session-core/src/test/java/org/springframework/session/config/annotation/web/http/EnableSpringHttpSessionCustomCookieSerializerTests.java index e36294b7e..4cfe7299b 100644 --- a/spring-session-core/src/test/java/org/springframework/session/config/annotation/web/http/EnableSpringHttpSessionCustomCookieSerializerTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/config/annotation/web/http/EnableSpringHttpSessionCustomCookieSerializerTests.java @@ -102,7 +102,7 @@ void usesReadSessionIds() throws Exception { @Test void usesWrite() throws Exception { - given(this.sessionRepository.createSession()).willReturn(new MapSession()); + given(this.sessionRepository.createSession()).willReturn(new MapSession("1")); this.sessionRepositoryFilter.doFilter(this.request, this.response, new MockFilterChain() { diff --git a/spring-session-core/src/test/java/org/springframework/session/web/http/CookieHttpSessionIdResolverTests.java b/spring-session-core/src/test/java/org/springframework/session/web/http/CookieHttpSessionIdResolverTests.java index 87fdf54a1..cd05ebbc2 100644 --- a/spring-session-core/src/test/java/org/springframework/session/web/http/CookieHttpSessionIdResolverTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/web/http/CookieHttpSessionIdResolverTests.java @@ -49,7 +49,7 @@ class CookieHttpSessionIdResolverTests { @BeforeEach void setup() { this.cookieName = "SESSION"; - this.session = new MapSession(); + this.session = new MapSession("1"); this.request = new MockHttpServletRequest(); this.response = new MockHttpServletResponse(); this.strategy = new CookieHttpSessionIdResolver(); @@ -91,7 +91,7 @@ void onNewSessionTwiceSameId() { @Test void onNewSessionTwiceNewId() { - Session newSession = new MapSession(); + Session newSession = new MapSession("2"); this.strategy.setSessionId(this.request, this.response, this.session.getId()); this.strategy.setSessionId(this.request, this.response, newSession.getId()); diff --git a/spring-session-core/src/test/java/org/springframework/session/web/http/SessionEventHttpSessionListenerAdapterTests.java b/spring-session-core/src/test/java/org/springframework/session/web/http/SessionEventHttpSessionListenerAdapterTests.java index 7a2be0ca6..c97819da6 100644 --- a/spring-session-core/src/test/java/org/springframework/session/web/http/SessionEventHttpSessionListenerAdapterTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/web/http/SessionEventHttpSessionListenerAdapterTests.java @@ -73,7 +73,7 @@ void setup() { this.listener = new SessionEventHttpSessionListenerAdapter(Arrays.asList(this.listener1, this.listener2)); this.listener.setServletContext(new MockServletContext()); - Session session = new MapSession(); + Session session = new MapSession("1"); this.destroyed = new SessionDestroyedEvent(this, session); this.created = new SessionCreatedEvent(this, session); } diff --git a/spring-session-core/src/test/java/org/springframework/session/web/http/SessionRepositoryFilterTests.java b/spring-session-core/src/test/java/org/springframework/session/web/http/SessionRepositoryFilterTests.java index 2b1a5f843..67ac8c9f6 100644 --- a/spring-session-core/src/test/java/org/springframework/session/web/http/SessionRepositoryFilterTests.java +++ b/spring-session-core/src/test/java/org/springframework/session/web/http/SessionRepositoryFilterTests.java @@ -133,7 +133,7 @@ public void doFilter(HttpServletRequest wrappedRequest) { @Test void doFilterCreateSetsLastAccessedTime() throws Exception { - MapSession session = new MapSession(); + MapSession session = new MapSession("1"); session.setLastAccessedTime(Instant.EPOCH); this.sessionRepository = spy(this.sessionRepository); given(this.sessionRepository.createSession()).willReturn(session); diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryITests.java index c58a96705..7c36afd09 100644 --- a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryITests.java +++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryITests.java @@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.data.redis.ReactiveRedisSessionRepository.RedisSession; import org.springframework.session.data.redis.config.annotation.web.server.EnableRedisWebSession; import org.springframework.test.context.ContextConfiguration; @@ -44,6 +45,8 @@ @WebAppConfiguration class ReactiveRedisSessionRepositoryITests extends AbstractRedisITests { + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Autowired private ReactiveRedisSessionRepository repository; @@ -114,7 +117,8 @@ void changeSessionIdWhenOnlyChangeId() { assertThat(findById.getAttribute(attrName)).isEqualTo(attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById).block(); @@ -132,8 +136,10 @@ void changeSessionIdWhenChangeTwice() { this.repository.save(toSave).block(); String originalId = toSave.getId(); - String changeId1 = toSave.changeSessionId(); - String changeId2 = toSave.changeSessionId(); + String changeId1 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId1); + String changeId2 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId2); this.repository.save(toSave).block(); @@ -156,7 +162,8 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { findById.setAttribute(attrName, attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById).block(); @@ -171,7 +178,7 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { void changeSessionIdWhenHasNotSaved() { RedisSession toSave = this.repository.createSession().block(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave).block(); @@ -184,7 +191,7 @@ void changeSessionIdWhenHasNotSaved() { void changeSessionIdSaveTwice() { RedisSession toSave = this.repository.createSession().block(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave).block(); this.repository.save(toSave).block(); @@ -202,7 +209,7 @@ void changeSessionSaveOldSessionInstance() { this.repository.save(toSave).block(); RedisSession session = this.repository.findById(sessionId).block(); - session.changeSessionId(); + session.changeSessionId("1"); session.setLastAccessedTime(Instant.now()); this.repository.save(session).block(); diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryITests.java index a649148f3..acba8860f 100644 --- a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryITests.java +++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryITests.java @@ -36,6 +36,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.data.SessionEventRegistry; import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession; import org.springframework.session.data.redis.config.annotation.SpringSessionRedisOperations; @@ -63,6 +64,8 @@ class RedisIndexedSessionRepositoryITests extends AbstractRedisITests { private static final String INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME; + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Autowired private RedisIndexedSessionRepository repository; @@ -487,7 +490,8 @@ void changeSessionIdWhenOnlyChangeId() { assertThat(findById.getAttribute(attrName)).isEqualTo(attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -505,8 +509,10 @@ void changeSessionIdWhenChangeTwice() { this.repository.save(toSave); String originalId = toSave.getId(); - String changeId1 = toSave.changeSessionId(); - String changeId2 = toSave.changeSessionId(); + String changeId1 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId1); + String changeId2 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId2); this.repository.save(toSave); @@ -529,7 +535,8 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { findById.setAttribute(attrName, attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -547,7 +554,7 @@ void changeSessionIdWhenHasNotSaved() { RedisSession toSave = this.repository.createSession(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave); @@ -560,7 +567,7 @@ void changeSessionIdWhenHasNotSaved() { void changeSessionIdSaveTwice() { RedisSession toSave = this.repository.createSession(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave); this.repository.save(toSave); @@ -578,7 +585,7 @@ void changeSessionIdWhenSessionIsDeleted() { this.repository.deleteById(sessionId); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave); assertThat(this.repository.findById(toSave.getId())).isNull(); @@ -594,9 +601,9 @@ void changeSessionIdSaveConcurrently() { RedisSession copy1 = this.repository.findById(originalId); RedisSession copy2 = this.repository.findById(originalId); - copy1.changeSessionId(); + copy1.changeSessionId("1"); this.repository.save(copy1); - copy2.changeSessionId(); + copy2.changeSessionId("2"); this.repository.save(copy2); assertThat(this.repository.findById(originalId)).isNull(); diff --git a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryITests.java b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryITests.java index cc2c0b51b..98ce30d0e 100644 --- a/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryITests.java +++ b/spring-session-data-redis/src/integration-test/java/org/springframework/session/data/redis/RedisSessionRepositoryITests.java @@ -31,6 +31,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.session.MapSession; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.config.annotation.web.http.EnableSpringHttpSession; import org.springframework.session.data.redis.RedisSessionRepository.RedisSession; import org.springframework.test.context.ContextConfiguration; @@ -53,6 +54,8 @@ class RedisSessionRepositoryITests extends AbstractRedisITests { @Autowired private RedisSessionRepository sessionRepository; + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Test void save_NewSession_ShouldSaveSession() { RedisSession session = createAndSaveSession(Instant.now()); @@ -98,7 +101,8 @@ void save_ChangeSessionIdAndUpdateAttribute_ShouldChangeSessionId() { RedisSession session = createAndSaveSession(Instant.now()); String originalSessionId = session.getId(); updateSession(session, Instant.now(), "attribute1", "value2"); - String newSessionId = session.changeSessionId(); + String newSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId); this.sessionRepository.save(session); RedisSession loaded = this.sessionRepository.findById(newSessionId); assertThat(loaded).isNotNull(); @@ -111,7 +115,8 @@ void save_ChangeSessionIdAndUpdateAttribute_ShouldChangeSessionId() { void save_OnlyChangeSessionId_ShouldChangeSessionId() { RedisSession session = createAndSaveSession(Instant.now()); String originalSessionId = session.getId(); - String newSessionId = session.changeSessionId(); + String newSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId); this.sessionRepository.save(session); assertThat(this.sessionRepository.findById(newSessionId)).isNotNull(); assertThat(this.sessionRepository.findById(originalSessionId)).isNull(); @@ -122,9 +127,11 @@ void save_ChangeSessionIdTwice_ShouldChangeSessionId() { RedisSession session = createAndSaveSession(Instant.now()); String originalSessionId = session.getId(); updateSession(session, Instant.now(), "attribute1", "value2"); - String newSessionId1 = session.changeSessionId(); + String newSessionId1 = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId1); updateSession(session, Instant.now(), "attribute1", "value3"); - String newSessionId2 = session.changeSessionId(); + String newSessionId2 = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId2); this.sessionRepository.save(session); assertThat(this.sessionRepository.findById(newSessionId1)).isNull(); assertThat(this.sessionRepository.findById(newSessionId2)).isNotNull(); @@ -136,7 +143,8 @@ void save_ChangeSessionIdOnNewSession_ShouldChangeSessionId() { RedisSession session = this.sessionRepository.createSession(); String originalSessionId = session.getId(); updateSession(session, Instant.now(), "attribute1", "value1"); - String newSessionId = session.changeSessionId(); + String newSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId); this.sessionRepository.save(session); assertThat(this.sessionRepository.findById(newSessionId)).isNotNull(); assertThat(this.sessionRepository.findById(originalSessionId)).isNull(); @@ -148,7 +156,8 @@ void save_ChangeSessionIdSaveTwice_ShouldChangeSessionId() { String originalSessionId; originalSessionId = session.getId(); updateSession(session, Instant.now(), "attribute1", "value1"); - String newSessionId = session.changeSessionId(); + String newSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId); this.sessionRepository.save(session); this.sessionRepository.save(session); assertThat(this.sessionRepository.findById(newSessionId)).isNotNull(); @@ -161,7 +170,8 @@ void save_ChangeSessionIdOnDeletedSession_ShouldThrowException() { String originalSessionId = session.getId(); this.sessionRepository.deleteById(originalSessionId); updateSession(session, Instant.now(), "attribute1", "value1"); - String newSessionId = session.changeSessionId(); + String newSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newSessionId); assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.save(session)) .withMessage("Session was invalidated"); assertThat(this.sessionRepository.findById(newSessionId)).isNull(); @@ -175,10 +185,12 @@ void save_ChangeSessionIdConcurrent_ShouldThrowException() { RedisSession copy2 = this.sessionRepository.findById(originalSessionId); Instant now = Instant.now().truncatedTo(ChronoUnit.MILLIS); updateSession(copy1, now.plusSeconds(1L), "attribute2", "value2"); - String newSessionId1 = copy1.changeSessionId(); + String newSessionId1 = this.sessionIdStrategy.createSessionId(); + copy1.changeSessionId(newSessionId1); this.sessionRepository.save(copy1); updateSession(copy2, now.plusSeconds(2L), "attribute3", "value3"); - String newSessionId2 = copy2.changeSessionId(); + String newSessionId2 = this.sessionIdStrategy.createSessionId(); + copy2.changeSessionId(newSessionId2); assertThatIllegalStateException().isThrownBy(() -> this.sessionRepository.save(copy2)) .withMessage("Session was invalidated"); assertThat(this.sessionRepository.findById(newSessionId1)).isNotNull(); diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java index 81e808243..9c07c9207 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/ReactiveRedisSessionRepository.java @@ -30,6 +30,7 @@ import org.springframework.session.ReactiveSessionRepository; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.util.Assert; /** @@ -37,6 +38,7 @@ * {@link ReactiveRedisOperations}. * * @author Vedran Pavic + * @author Jakub Maciej * @since 2.2.0 */ public class ReactiveRedisSessionRepository @@ -49,6 +51,8 @@ public class ReactiveRedisSessionRepository private final ReactiveRedisOperations sessionRedisOperations; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + /** * The namespace for every key used by Spring Session in Redis. */ @@ -108,7 +112,7 @@ public ReactiveRedisOperations getSessionRedisOperations() { @Override public Mono createSession() { return Mono.defer(() -> { - MapSession cached = new MapSession(); + MapSession cached = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } @@ -157,6 +161,16 @@ private String getSessionKey(String sessionId) { return this.namespace + "sessions:" + sessionId; } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + /** * A custom implementation of {@link Session} that uses a {@link MapSession} as the * basis for its mapping. It keeps track of any attributes that have changed. When @@ -195,8 +209,8 @@ public String getId() { } @Override - public String changeSessionId() { - return this.cached.changeSessionId(); + public void changeSessionId(final String id) { + this.cached.changeSessionId(id); } @Override diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java index 46442683e..de1833e93 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisIndexedSessionRepository.java @@ -44,6 +44,7 @@ import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; import org.springframework.session.events.SessionDestroyedEvent; @@ -244,6 +245,7 @@ * * @author Rob Winch * @author Vedran Pavic + * @author Jakub Maciej * @since 2.2.0 */ public class RedisIndexedSessionRepository @@ -263,6 +265,8 @@ public class RedisIndexedSessionRepository */ public static final String DEFAULT_NAMESPACE = "spring:session"; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + private int database = DEFAULT_DATABASE; /** @@ -487,9 +491,16 @@ public void deleteById(String sessionId) { save(session); } + @Override + public String changeSessionId(final RedisSession session) { + String newId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newId); + return newId; + } + @Override public RedisSession createSession() { - MapSession cached = new MapSession(); + MapSession cached = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } @@ -659,6 +670,16 @@ static String getSessionAttrNameKey(String attributeName) { return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName; } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + /** * A custom implementation of {@link Session} that uses a {@link MapSession} as the * basis for its mapping. It keeps track of any attributes that have changed. When @@ -722,8 +743,8 @@ public String getId() { } @Override - public String changeSessionId() { - return this.cached.changeSessionId(); + public void changeSessionId(final String id) { + this.cached.changeSessionId(id); } @Override diff --git a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionRepository.java b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionRepository.java index cdbb52c9d..14ddf1ef8 100644 --- a/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionRepository.java +++ b/spring-session-data-redis/src/main/java/org/springframework/session/data/redis/RedisSessionRepository.java @@ -28,6 +28,7 @@ import org.springframework.session.MapSession; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.SessionRepository; import org.springframework.util.Assert; @@ -38,6 +39,7 @@ * This implementation does not support publishing of session events. * * @author Vedran Pavic + * @author Jakub Maciej * @since 2.2.0 */ public class RedisSessionRepository implements SessionRepository { @@ -46,6 +48,8 @@ public class RedisSessionRepository implements SessionRepository sessionRedisOperations; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + private Duration defaultMaxInactiveInterval = Duration.ofSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS); private String keyNamespace = DEFAULT_KEY_NAMESPACE; @@ -102,7 +106,7 @@ public void setSaveMode(SaveMode saveMode) { @Override public RedisSession createSession() { - MapSession cached = new MapSession(); + MapSession cached = new MapSession(this.sessionIdStrategy.createSessionId()); cached.setMaxInactiveInterval(this.defaultMaxInactiveInterval); RedisSession session = new RedisSession(cached, true); session.flushIfRequired(); @@ -142,6 +146,11 @@ public void deleteById(String sessionId) { this.sessionRedisOperations.delete(key); } + @Override + public String changeSessionId(final RedisSession session) { + return null; + } + /** * Returns the {@link RedisOperations} used for sessions. * @return the {@link RedisOperations} used for sessions @@ -158,6 +167,16 @@ private static String getAttributeKey(String attributeName) { return RedisSessionMapper.ATTRIBUTE_PREFIX + attributeName; } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + /** * An internal {@link Session} implementation used by this {@link SessionRepository}. */ @@ -193,8 +212,8 @@ public String getId() { } @Override - public String changeSessionId() { - return this.cached.changeSessionId(); + public void changeSessionId(final String id) { + this.cached.changeSessionId(id); } @Override diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryTests.java index da58aadf9..de1fee8aa 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/ReactiveRedisSessionRepositoryTests.java @@ -69,7 +69,7 @@ class ReactiveRedisSessionRepositoryTests { void setUp() { this.repository = new ReactiveRedisSessionRepository(this.redisOperations); - this.cached = new MapSession(); + this.cached = new MapSession("1"); this.cached.setId("session-id"); this.cached.setCreationTime(Instant.ofEpochMilli(1404360000000L)); this.cached.setLastAccessedTime(Instant.ofEpochMilli(1404360000000L)); @@ -131,7 +131,7 @@ void saveNewSession() { given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true)); given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true)); - RedisSession newSession = this.repository.new RedisSession(new MapSession(), true); + RedisSession newSession = this.repository.new RedisSession(new MapSession("1"), true); StepVerifier.create(this.repository.save(newSession)).verifyComplete(); verify(this.redisOperations).opsForHash(); @@ -217,7 +217,7 @@ void saveRemoveAttribute() { given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true)); String attrName = "attrName"; - RedisSession session = this.repository.new RedisSession(new MapSession(), false); + RedisSession session = this.repository.new RedisSession(new MapSession("1"), false); session.removeAttribute(attrName); StepVerifier.create(this.repository.save(session)).verifyComplete(); @@ -344,7 +344,7 @@ void saveWithSaveModeOnSetAttribute() { given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true)); given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true)); this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -368,7 +368,7 @@ void saveWithSaveModeOnGetAttribute() { given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true)); given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true)); this.repository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -392,7 +392,7 @@ void saveWithSaveModeAlways() { given(this.hashOperations.putAll(anyString(), any())).willReturn(Mono.just(true)); given(this.redisOperations.expire(anyString(), any())).willReturn(Mono.just(true)); this.repository.setSaveMode(SaveMode.ALWAYS); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryTests.java index 658c8a456..89b168dc0 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisIndexedSessionRepositoryTests.java @@ -48,6 +48,7 @@ import org.springframework.session.MapSession; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.data.redis.RedisIndexedSessionRepository.RedisSession; import org.springframework.session.events.AbstractSessionEvent; @@ -67,6 +68,8 @@ class RedisIndexedSessionRepositoryTests { + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Mock private RedisOperations redisOperations; @@ -101,7 +104,7 @@ void setup() { this.redisRepository = new RedisIndexedSessionRepository(this.redisOperations); this.redisRepository.setDefaultSerializer(this.defaultSerializer); - this.cached = new MapSession(); + this.cached = new MapSession("1"); this.cached.setId("session-id"); this.cached.setCreationTime(Instant.ofEpochMilli(1404360000000L)); this.cached.setLastAccessedTime(Instant.ofEpochMilli(1404360000000L)); @@ -121,7 +124,8 @@ void changeSessionIdWhenNotSaved() { RedisSession createSession = this.redisRepository.createSession(); String originalId = createSession.getId(); - String changeSessionId = createSession.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + createSession.changeSessionId(changeSessionId); this.redisRepository.save(createSession); verify(this.redisOperations, never()).rename(anyString(), anyString()); @@ -139,7 +143,8 @@ void changeSessionIdWhenSaved() { RedisSession session = this.redisRepository.new RedisSession(this.cached, false); session.setLastAccessedTime(session.getLastAccessedTime()); String originalId = session.getId(); - String changeSessionId = session.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(changeSessionId); this.redisRepository.save(session); verify(this.redisOperations, times(2)).rename(anyString(), anyString()); @@ -151,7 +156,7 @@ void changeSessionIdWhenSaved() { @Test void createSessionDefaultMaxInactiveInterval() { Session session = this.redisRepository.createSession(); - assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession("1").getMaxInactiveInterval()); } @Test @@ -257,7 +262,7 @@ void saveLastAccessChanged() { @Test void saveSetAttribute() { String attrName = "attrName"; - RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false); + RedisSession session = this.redisRepository.new RedisSession(new MapSession("1"), false); session.setAttribute(attrName, "attrValue"); given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations); given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); @@ -272,7 +277,7 @@ void saveSetAttribute() { @Test void saveRemoveAttribute() { String attrName = "attrName"; - RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false); + RedisSession session = this.redisRepository.new RedisSession(new MapSession("1"), false); session.removeAttribute(attrName); given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations); given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); @@ -285,7 +290,7 @@ void saveRemoveAttribute() { @Test void saveExpired() { - RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false); + RedisSession session = this.redisRepository.new RedisSession(new MapSession("1"), false); session.setMaxInactiveInterval(Duration.ZERO); given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations); given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); @@ -312,7 +317,7 @@ void redisSessionGetAttributes() { @SuppressWarnings("unchecked") void delete() { String attrName = "attrName"; - MapSession expected = new MapSession(); + MapSession expected = new MapSession("1"); expected.setLastAccessedTime(Instant.now().minusSeconds(60)); expected.setAttribute(attrName, "attrValue"); given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations); @@ -357,7 +362,7 @@ void getSessionNotFound() { void getSessionFound() { String attribute1 = "attribute1"; String attribute2 = "attribute2"; - MapSession expected = new MapSession(); + MapSession expected = new MapSession("1"); expected.setLastAccessedTime(Instant.now().minusSeconds(60)); expected.setAttribute(attribute1, "test"); expected.setAttribute(attribute2, null); @@ -739,7 +744,7 @@ void setFlushModeNull() { void changeRedisNamespace() { String namespace = "foo:bar"; this.redisRepository.setRedisKeyNamespace(namespace); - RedisSession session = this.redisRepository.new RedisSession(new MapSession(), false); + RedisSession session = this.redisRepository.new RedisSession(new MapSession("1"), false); session.setMaxInactiveInterval(Duration.ZERO); given(this.redisOperations.boundHashOps(anyString())).willReturn(this.boundHashOperations); given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); @@ -835,7 +840,7 @@ void saveWithSaveModeOnSetAttribute() { given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations); this.redisRepository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -852,7 +857,7 @@ void saveWithSaveModeOnGetAttribute() { given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations); this.redisRepository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -869,7 +874,7 @@ void saveWithSaveModeAlways() { given(this.redisOperations.boundSetOps(anyString())).willReturn(this.boundSetOperations); given(this.redisOperations.boundValueOps(anyString())).willReturn(this.boundValueOperations); this.redisRepository.setSaveMode(SaveMode.ALWAYS); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java index af62e4b0c..87b3d3153 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionExpirationPolicyTests.java @@ -69,7 +69,7 @@ void setup() { RedisIndexedSessionRepository repository = new RedisIndexedSessionRepository(this.sessionRedisOperations); this.policy = new RedisSessionExpirationPolicy(this.sessionRedisOperations, repository::getExpirationsKey, repository::getSessionKey); - this.session = new MapSession(); + this.session = new MapSession("1"); this.session.setLastAccessedTime(Instant.ofEpochMilli(1429116694675L)); this.session.setId("12345"); diff --git a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java index 9ffda6d28..f8218b417 100644 --- a/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java +++ b/spring-session-data-redis/src/test/java/org/springframework/session/data/redis/RedisSessionRepositoryTests.java @@ -200,7 +200,7 @@ void save_NewSessionAndCustomKeyNamespace_ShouldSaveSession() { @Test void save_NewSessionAndChangedSessionId_ShouldSaveSession() { RedisSession session = this.sessionRepository.createSession(); - session.changeSessionId(); + session.changeSessionId("1"); this.sessionRepository.save(session); String key = getSessionKey(session.getId()); verify(this.sessionRedisOperations).opsForHash(); diff --git a/spring-session-docs/src/test/java/docs/http/AbstractHttpSessionListenerTests.java b/spring-session-docs/src/test/java/docs/http/AbstractHttpSessionListenerTests.java index 4b5047f84..7bd5a1360 100644 --- a/spring-session-docs/src/test/java/docs/http/AbstractHttpSessionListenerTests.java +++ b/spring-session-docs/src/test/java/docs/http/AbstractHttpSessionListenerTests.java @@ -54,7 +54,7 @@ public abstract class AbstractHttpSessionListenerTests { @Test void springSessionDestroyedTranslatedToSpringSecurityDestroyed() { - Session session = new MapSession(); + Session session = new MapSession("1"); this.publisher.publishEvent(new org.springframework.session.events.SessionDestroyedEvent(this, session)); diff --git a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastIndexedSessionRepositoryITests.java b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastIndexedSessionRepositoryITests.java index fa2584461..99b1c4e3b 100644 --- a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastIndexedSessionRepositoryITests.java +++ b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/AbstractHazelcastIndexedSessionRepositoryITests.java @@ -30,6 +30,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.MapSession; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.hazelcast.HazelcastIndexedSessionRepository.HazelcastSession; import static org.assertj.core.api.Assertions.assertThat; @@ -44,6 +45,8 @@ abstract class AbstractHazelcastIndexedSessionRepositoryITests { private static final String SPRING_SECURITY_CONTEXT = "SPRING_SECURITY_CONTEXT"; + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Autowired private HazelcastInstance hazelcastInstance; @@ -84,7 +87,8 @@ void changeSessionIdWhenOnlyChangeId() { assertThat(findById.getAttribute(attrName)).isEqualTo(attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -104,8 +108,10 @@ void changeSessionIdWhenChangeTwice() { this.repository.save(toSave); String originalId = toSave.getId(); - String changeId1 = toSave.changeSessionId(); - String changeId2 = toSave.changeSessionId(); + String changeId1 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId1); + String changeId2 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId2); this.repository.save(toSave); @@ -130,7 +136,8 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { findById.setAttribute(attrName, attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -147,7 +154,7 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { void changeSessionIdWhenHasNotSaved() { HazelcastSession toSave = this.repository.createSession(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave); diff --git a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/SessionEventHazelcastIndexedSessionRepositoryTests.java b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/SessionEventHazelcastIndexedSessionRepositoryTests.java index c0309f02b..6cd86781f 100644 --- a/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/SessionEventHazelcastIndexedSessionRepositoryTests.java +++ b/spring-session-hazelcast/src/integration-test/java/org/springframework/session/hazelcast/SessionEventHazelcastIndexedSessionRepositoryTests.java @@ -52,6 +52,7 @@ * * @author Tommy Ludwig * @author Vedran Pavic + * @author Jakub Maciej */ @ExtendWith(SpringExtension.class) @ContextConfiguration @@ -172,7 +173,7 @@ void changeSessionIdNoEventTest() throws InterruptedException { .isInstanceOf(SessionCreatedEvent.class); this.registry.clear(); - sessionToSave.changeSessionId(); + sessionToSave.changeSessionId("1"); this.repository.save(sessionToSave); assertThat(this.registry.receivedEvent(sessionToSave.getId())).isFalse(); diff --git a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepository.java b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepository.java index 4de118c64..348d08b1e 100644 --- a/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepository.java +++ b/spring-session-hazelcast/src/main/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepository.java @@ -47,6 +47,7 @@ import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.events.AbstractSessionEvent; import org.springframework.session.events.SessionCreatedEvent; import org.springframework.session.events.SessionDeletedEvent; @@ -108,6 +109,7 @@ * @author Tommy Ludwig * @author Mark Anderson * @author Aleksandar Stojsavljevic + * @author Jakub Maciej * @since 2.2.0 */ public class HazelcastIndexedSessionRepository @@ -133,6 +135,8 @@ public class HazelcastIndexedSessionRepository private final HazelcastInstance hazelcastInstance; + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + private ApplicationEventPublisher eventPublisher = (event) -> { }; @@ -234,7 +238,7 @@ public void setSaveMode(SaveMode saveMode) { @Override public HazelcastSession createSession() { - MapSession cached = new MapSession(); + MapSession cached = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { cached.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } @@ -296,6 +300,13 @@ public void deleteById(String id) { this.sessions.remove(id); } + @Override + public String changeSessionId(final HazelcastSession session) { + String newId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newId); + return newId; + } + @Override public Map findByIndexNameAndIndexValue(String indexName, String indexValue) { if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) { @@ -339,6 +350,16 @@ public void entryRemoved(EntryEvent event) { } } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + /** * A custom implementation of {@link Session} that uses a {@link MapSession} as the * basis for its mapping. It keeps track if changes have been made since last save. @@ -394,10 +415,10 @@ public String getId() { } @Override - public String changeSessionId() { - String newSessionId = this.delegate.changeSessionId(); + public void changeSessionId(final String id) { + this.delegate.changeSessionId(id); this.sessionIdChanged = true; - return newSessionId; + flushImmediateIfNecessary(); } @Override diff --git a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java index f648bfe58..e5e20d6eb 100644 --- a/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java +++ b/spring-session-hazelcast/src/test/java/org/springframework/session/hazelcast/HazelcastIndexedSessionRepositoryTests.java @@ -98,7 +98,7 @@ void createSessionDefaultMaxInactiveInterval() { HazelcastSession session = this.repository.createSession(); - assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession("1").getMaxInactiveInterval()); verifyZeroInteractions(this.sessions); } @@ -306,7 +306,7 @@ void getSessionNotFound() { void getSessionExpired() { verify(this.sessions, times(1)).addEntryListener(any(MapListener.class), anyBoolean()); - MapSession expired = new MapSession(); + MapSession expired = new MapSession("1"); expired.setLastAccessedTime(Instant.now().minusSeconds(MapSession.DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS + 1)); given(this.sessions.get(eq(expired.getId()))).willReturn(expired); @@ -322,7 +322,7 @@ void getSessionExpired() { void getSessionFound() { verify(this.sessions, times(1)).addEntryListener(any(MapListener.class), anyBoolean()); - MapSession saved = new MapSession(); + MapSession saved = new MapSession("1"); saved.setAttribute("savedName", "savedValue"); given(this.sessions.get(eq(saved.getId()))).willReturn(saved); @@ -381,10 +381,10 @@ void findByIndexNameAndIndexValuePrincipalIndexNameFound() { Authentication authentication = new UsernamePasswordAuthenticationToken(principal, "notused", AuthorityUtils.createAuthorityList("ROLE_USER")); List saved = new ArrayList<>(2); - MapSession saved1 = new MapSession(); + MapSession saved1 = new MapSession("1"); saved1.setAttribute(SPRING_SECURITY_CONTEXT, authentication); saved.add(saved1); - MapSession saved2 = new MapSession(); + MapSession saved2 = new MapSession("2"); saved2.setAttribute(SPRING_SECURITY_CONTEXT, authentication); saved.add(saved2); given(this.sessions.values(isA(EqualPredicate.class))).willReturn(saved); @@ -415,7 +415,7 @@ void getAttributeNamesAndRemove() { void saveWithSaveModeOnSetAttribute() { verify(this.sessions).addEntryListener(any(MapListener.class), anyBoolean()); this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -434,7 +434,7 @@ void saveWithSaveModeOnSetAttribute() { void saveWithSaveModeOnGetAttribute() { verify(this.sessions).addEntryListener(any(MapListener.class), anyBoolean()); this.repository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); @@ -453,7 +453,7 @@ void saveWithSaveModeOnGetAttribute() { void saveWithSaveModeAlways() { verify(this.sessions).addEntryListener(any(MapListener.class), anyBoolean()); this.repository.setSaveMode(SaveMode.ALWAYS); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", "value1"); delegate.setAttribute("attribute2", "value2"); delegate.setAttribute("attribute3", "value3"); diff --git a/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcIndexedSessionRepositoryITests.java b/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcIndexedSessionRepositoryITests.java index 9dc583db7..400a3d07c 100644 --- a/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcIndexedSessionRepositoryITests.java +++ b/spring-session-jdbc/src/integration-test/java/org/springframework/session/jdbc/AbstractJdbcIndexedSessionRepositoryITests.java @@ -38,6 +38,7 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.session.FindByIndexNameSessionRepository; import org.springframework.session.MapSession; +import org.springframework.session.SessionIdStrategy; import org.springframework.session.jdbc.JdbcIndexedSessionRepository.JdbcSession; import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession; import org.springframework.test.util.ReflectionTestUtils; @@ -57,6 +58,8 @@ abstract class AbstractJdbcIndexedSessionRepositoryITests { private static final String INDEX_NAME = FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME; + private final SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + @Autowired private JdbcIndexedSessionRepository repository; @@ -578,7 +581,8 @@ void changeSessionIdWhenOnlyChangeId() { assertThat(findById.getAttribute(attrName)).isEqualTo(attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -598,8 +602,10 @@ void changeSessionIdWhenChangeTwice() { this.repository.save(toSave); String originalId = toSave.getId(); - String changeId1 = toSave.changeSessionId(); - String changeId2 = toSave.changeSessionId(); + String changeId1 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId1); + String changeId2 = this.sessionIdStrategy.createSessionId(); + toSave.changeSessionId(changeId2); this.repository.save(toSave); @@ -622,7 +628,8 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { findById.setAttribute(attrName, attrValue); String originalFindById = findById.getId(); - String changeSessionId = findById.changeSessionId(); + String changeSessionId = this.sessionIdStrategy.createSessionId(); + findById.changeSessionId(changeSessionId); this.repository.save(findById); @@ -639,7 +646,7 @@ void changeSessionIdWhenSetAttributeOnChangedSession() { void changeSessionIdWhenHasNotSaved() { JdbcSession toSave = this.repository.createSession(); String originalId = toSave.getId(); - toSave.changeSessionId(); + toSave.changeSessionId("1"); this.repository.save(toSave); @@ -647,7 +654,8 @@ void changeSessionIdWhenHasNotSaved() { assertThat(this.repository.findById(originalId)).isNull(); } - @Test // gh-1070 + @Test + // gh-1070 void saveUpdatedAddAndModifyAttribute() { JdbcSession session = this.repository.createSession(); this.repository.save(session); @@ -660,7 +668,8 @@ void saveUpdatedAddAndModifyAttribute() { assertThat(session.getAttribute("testName")).isEqualTo("testValue2"); } - @Test // gh-1070 + @Test + // gh-1070 void saveUpdatedAddAndRemoveAttribute() { JdbcSession session = this.repository.createSession(); this.repository.save(session); @@ -673,7 +682,8 @@ void saveUpdatedAddAndRemoveAttribute() { assertThat(session.getAttribute("testName")).isNull(); } - @Test // gh-1070 + @Test + // gh-1070 void saveUpdatedModifyAndRemoveAttribute() { JdbcSession session = this.repository.createSession(); session.setAttribute("testName", "testValue1"); @@ -687,7 +697,8 @@ void saveUpdatedModifyAndRemoveAttribute() { assertThat(session.getAttribute("testName")).isNull(); } - @Test // gh-1070 + @Test + // gh-1070 void saveUpdatedRemoveAndAddAttribute() { JdbcSession session = this.repository.createSession(); session.setAttribute("testName", "testValue1"); @@ -701,7 +712,8 @@ void saveUpdatedRemoveAndAddAttribute() { assertThat(session.getAttribute("testName")).isEqualTo("testValue2"); } - @Test // gh-1031 + @Test + // gh-1031 void saveDeleted() { JdbcSession session = this.repository.createSession(); this.repository.save(session); @@ -713,7 +725,8 @@ void saveDeleted() { assertThat(this.repository.findById(session.getId())).isNull(); } - @Test // gh-1031 + @Test + // gh-1031 void saveDeletedAddAttribute() { JdbcSession session = this.repository.createSession(); this.repository.save(session); @@ -726,7 +739,8 @@ void saveDeletedAddAttribute() { assertThat(this.repository.findById(session.getId())).isNull(); } - @Test // gh-1133 + @Test + // gh-1133 void sessionFromStoreResolvesAttributesLazily() { JdbcSession session = this.repository.createSession(); session.setAttribute("attribute1", "value1"); @@ -745,7 +759,8 @@ void sessionFromStoreResolvesAttributesLazily() { assertThat(ReflectionTestUtils.getField(attribute2, "value")).isEqualTo("value2"); } - @Test // gh-1203 + @Test + // gh-1203 void saveWithLargeAttribute() { String attributeName = "largeAttribute"; int arraySize = 4000; diff --git a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java index ea6f2efd0..1657e4d4c 100644 --- a/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java +++ b/spring-session-jdbc/src/main/java/org/springframework/session/jdbc/JdbcIndexedSessionRepository.java @@ -53,6 +53,7 @@ import org.springframework.session.PrincipalNameIndexResolver; import org.springframework.session.SaveMode; import org.springframework.session.Session; +import org.springframework.session.SessionIdStrategy; import org.springframework.transaction.support.TransactionOperations; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -125,6 +126,7 @@ * * @author Vedran Pavic * @author Craig Andrews + * @author Jakub Maciej * @since 2.2.0 */ public class JdbcIndexedSessionRepository @@ -198,6 +200,8 @@ public class JdbcIndexedSessionRepository private final ResultSetExtractor> extractor = new SessionResultSetExtractor(); + private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault(); + /** * The name of database table used by Spring Session to store sessions. */ @@ -395,7 +399,7 @@ public void setSaveMode(SaveMode saveMode) { @Override public JdbcSession createSession() { - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession(this.sessionIdStrategy.createSessionId()); if (this.defaultMaxInactiveInterval != null) { delegate.setMaxInactiveInterval(Duration.ofSeconds(this.defaultMaxInactiveInterval)); } @@ -438,6 +442,13 @@ public void deleteById(final String id) { .update(JdbcIndexedSessionRepository.this.deleteSessionQuery, id)); } + @Override + public String changeSessionId(final JdbcSession session) { + String newId = this.sessionIdStrategy.createSessionId(); + session.changeSessionId(newId); + return newId; + } + @Override public Map findByIndexNameAndIndexValue(String indexName, final String indexValue) { if (!PRINCIPAL_NAME_INDEX_NAME.equals(indexName)) { @@ -595,6 +606,16 @@ private Object deserialize(byte[] bytes) { TypeDescriptor.valueOf(Object.class)); } + /** + * Allows override of default session id generation strategy. + * @param sessionIdStrategy session id generation strategy to be used with this + * repository + */ + public void setSessionIdStrategy(final SessionIdStrategy sessionIdStrategy) { + Assert.notNull(sessionIdStrategy, "sessionIdStrategy must not be null"); + this.sessionIdStrategy = sessionIdStrategy; + } + private enum DeltaValue { ADDED, UPDATED, REMOVED @@ -677,9 +698,9 @@ public String getId() { } @Override - public String changeSessionId() { + public void changeSessionId(final String id) { this.changed = true; - return this.delegate.changeSessionId(); + this.delegate.changeSessionId(id); } @Override diff --git a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java index aed37e535..b7d53dfdd 100644 --- a/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java +++ b/spring-session-jdbc/src/test/java/org/springframework/session/jdbc/JdbcIndexedSessionRepositoryTests.java @@ -242,7 +242,7 @@ void createSessionDefaultMaxInactiveInterval() { JdbcSession session = this.repository.createSession(); assertThat(session.isNew()).isTrue(); - assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession().getMaxInactiveInterval()); + assertThat(session.getMaxInactiveInterval()).isEqualTo(new MapSession("1").getMaxInactiveInterval()); verifyNoMoreInteractions(this.jdbcOperations); } @@ -311,7 +311,7 @@ void saveNewWithMultipleAttributes() { @Test void saveUpdatedAddSingleAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue"); this.repository.save(session); @@ -324,7 +324,7 @@ void saveUpdatedAddSingleAttribute() { @Test void saveUpdatedAddMultipleAttributes() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName1", "testValue1"); session.setAttribute("testName2", "testValue2"); @@ -338,7 +338,7 @@ void saveUpdatedAddMultipleAttributes() { @Test void saveUpdatedModifySingleAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue"); session.clearChangeFlags(); session.setAttribute("testName", "testValue"); @@ -353,7 +353,7 @@ void saveUpdatedModifySingleAttribute() { @Test void saveUpdatedModifyMultipleAttributes() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName1", "testValue1"); session.setAttribute("testName2", "testValue2"); session.clearChangeFlags(); @@ -370,7 +370,7 @@ void saveUpdatedModifyMultipleAttributes() { @Test void saveUpdatedRemoveSingleAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue"); session.clearChangeFlags(); session.removeAttribute("testName"); @@ -385,7 +385,7 @@ void saveUpdatedRemoveSingleAttribute() { @Test void saveUpdatedRemoveNonExistingAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.removeAttribute("testName"); this.repository.save(session); @@ -396,7 +396,7 @@ void saveUpdatedRemoveNonExistingAttribute() { @Test void saveUpdatedRemoveMultipleAttributes() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName1", "testValue1"); session.setAttribute("testName2", "testValue2"); session.clearChangeFlags(); @@ -413,7 +413,7 @@ void saveUpdatedRemoveMultipleAttributes() { @Test // gh-1070 void saveUpdatedAddAndModifyAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue1"); session.setAttribute("testName", "testValue2"); @@ -427,7 +427,7 @@ void saveUpdatedAddAndModifyAttribute() { @Test // gh-1070 void saveUpdatedAddAndRemoveAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue"); session.removeAttribute("testName"); @@ -439,7 +439,7 @@ void saveUpdatedAddAndRemoveAttribute() { @Test // gh-1070 void saveUpdatedModifyAndRemoveAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue1"); session.clearChangeFlags(); session.setAttribute("testName", "testValue2"); @@ -455,7 +455,7 @@ void saveUpdatedModifyAndRemoveAttribute() { @Test // gh-1070 void saveUpdatedRemoveAndAddAttribute() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setAttribute("testName", "testValue1"); session.clearChangeFlags(); session.removeAttribute("testName"); @@ -471,7 +471,7 @@ void saveUpdatedRemoveAndAddAttribute() { @Test void saveUpdatedLastAccessedTime() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setLastAccessedTime(Instant.now()); this.repository.save(session); @@ -484,7 +484,7 @@ void saveUpdatedLastAccessedTime() { @Test void saveUnchanged() { - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); this.repository.save(session); @@ -525,7 +525,7 @@ void getSessionExpired() { @Test @SuppressWarnings("unchecked") void getSessionFound() { - Session saved = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + Session saved = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); saved.setAttribute("savedName", "savedValue"); given(this.jdbcOperations.query(isA(String.class), isA(PreparedStatementSetter.class), isA(ResultSetExtractor.class))).willReturn(Collections.singletonList(saved)); @@ -620,7 +620,7 @@ void getAttributeNamesAndRemove() { @Test void saveWithSaveModeOnSetAttribute() { this.repository.setSaveMode(SaveMode.ON_SET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", (Supplier) () -> "value1"); delegate.setAttribute("attribute2", (Supplier) () -> "value2"); delegate.setAttribute("attribute3", (Supplier) () -> "value3"); @@ -636,7 +636,7 @@ void saveWithSaveModeOnSetAttribute() { @Test void saveWithSaveModeOnGetAttribute() { this.repository.setSaveMode(SaveMode.ON_GET_ATTRIBUTE); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", (Supplier) () -> "value1"); delegate.setAttribute("attribute2", (Supplier) () -> "value2"); delegate.setAttribute("attribute3", (Supplier) () -> "value3"); @@ -654,7 +654,7 @@ void saveWithSaveModeOnGetAttribute() { @Test void saveWithSaveModeAlways() { this.repository.setSaveMode(SaveMode.ALWAYS); - MapSession delegate = new MapSession(); + MapSession delegate = new MapSession("1"); delegate.setAttribute("attribute1", (Supplier) () -> "value1"); delegate.setAttribute("attribute2", (Supplier) () -> "value2"); delegate.setAttribute("attribute3", (Supplier) () -> "value3"); @@ -672,7 +672,7 @@ void saveWithSaveModeAlways() { @Test void flushModeImmediateSetAttribute() { this.repository.setFlushMode(FlushMode.IMMEDIATE); - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); String attrName = "someAttribute"; session.setAttribute(attrName, "someValue"); verify(this.jdbcOperations).update(startsWith("INSERT INTO SPRING_SESSION_ATTRIBUTES("), @@ -683,7 +683,7 @@ void flushModeImmediateSetAttribute() { @Test void flushModeImmediateRemoveAttribute() { this.repository.setFlushMode(FlushMode.IMMEDIATE); - MapSession cached = new MapSession(); + MapSession cached = new MapSession("1"); cached.setAttribute("attribute1", "value1"); JdbcSession session = this.repository.new JdbcSession(cached, "primaryKey", false); session.removeAttribute("attribute1"); @@ -695,7 +695,7 @@ void flushModeImmediateRemoveAttribute() { @Test void flushModeSetMaxInactiveIntervalInSeconds() { this.repository.setFlushMode(FlushMode.IMMEDIATE); - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setMaxInactiveInterval(Duration.ofSeconds(1)); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations); @@ -704,7 +704,7 @@ void flushModeSetMaxInactiveIntervalInSeconds() { @Test void flushModeSetLastAccessedTime() { this.repository.setFlushMode(FlushMode.IMMEDIATE); - JdbcSession session = this.repository.new JdbcSession(new MapSession(), "primaryKey", false); + JdbcSession session = this.repository.new JdbcSession(new MapSession("1"), "primaryKey", false); session.setLastAccessedTime(Instant.now()); verify(this.jdbcOperations).update(startsWith("UPDATE SPRING_SESSION SET"), isA(PreparedStatementSetter.class)); verifyNoMoreInteractions(this.jdbcOperations);