Add Session.changeSessionId

This commit is contained in:
Rob Winch
2017-07-20 15:03:18 -05:00
parent 2aa71ffb6d
commit be2604ca69
29 changed files with 576 additions and 169 deletions

View File

@@ -39,7 +39,7 @@ import org.springframework.session.events.SessionExpiredEvent;
* @author Rob Winch
* @since 2.0
*/
public class MapReactorSessionRepository implements ReactorSessionRepository<Session> {
public class MapReactorSessionRepository implements ReactorSessionRepository<MapSession> {
/**
* If non-null, this value is used to override
* {@link Session#setMaxInactiveInterval(Duration)}.
@@ -80,7 +80,7 @@ public class MapReactorSessionRepository implements ReactorSessionRepository<Ses
}
this.sessions = new ConcurrentHashMap<>();
for (Session session : sessions) {
this.performSave(session);
this.performSave(new MapSession(session));
}
}
@@ -96,7 +96,7 @@ public class MapReactorSessionRepository implements ReactorSessionRepository<Ses
}
this.sessions = new ConcurrentHashMap<>();
for (Session session : sessions) {
this.performSave(session);
this.performSave(new MapSession(session));
}
}
@@ -110,15 +110,19 @@ public class MapReactorSessionRepository implements ReactorSessionRepository<Ses
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);
}
public Mono<Void> save(Session session) {
public Mono<Void> save(MapSession session) {
return Mono.fromRunnable(() -> performSave(session));
}
private void performSave(Session session) {
private void performSave(MapSession session) {
if (!session.getId().equals(session.getOriginalId())) {
this.sessions.remove(session.getOriginalId());
session.setOriginalId(session.getId());
}
this.sessions.put(session.getId(), new MapSession(session));
}
public Mono<Session> findById(String id) {
public Mono<MapSession> findById(String id) {
return Mono.defer(() -> {
Session saved = this.sessions.get(id);
if (saved == null) {
@@ -136,9 +140,9 @@ public class MapReactorSessionRepository implements ReactorSessionRepository<Ses
return Mono.fromRunnable(() -> this.sessions.remove(id));
}
public Mono<Session> createSession() {
public Mono<MapSession> createSession() {
return Mono.defer(() -> {
Session result = new MapSession();
MapSession result = new MapSession();
if (this.defaultMaxInactiveInterval != null) {
result.setMaxInactiveInterval(
Duration.ofSeconds(this.defaultMaxInactiveInterval));

View File

@@ -52,6 +52,7 @@ public final class MapSession implements Session, Serializable {
public static final int DEFAULT_MAX_INACTIVE_INTERVAL_SECONDS = 1800;
private String id;
private String originalId;
private Map<String, Object> sessionAttrs = new HashMap<>();
private Instant creationTime = Instant.now();
private Instant lastAccessedTime = this.creationTime;
@@ -65,9 +66,10 @@ public final class MapSession implements Session, Serializable {
* Creates a new instance with a secure randomly generated identifier.
*/
public MapSession() {
this(UUID.randomUUID().toString());
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
@@ -77,6 +79,7 @@ public final class MapSession implements Session, Serializable {
*/
public MapSession(String id) {
this.id = id;
this.originalId = id;
}
/**
@@ -90,6 +93,7 @@ public final class MapSession implements Session, Serializable {
throw new IllegalArgumentException("session cannot be null");
}
this.id = session.getId();
this.originalId = this.id;
this.sessionAttrs = new HashMap<>(
session.getAttributeNames().size());
for (String attrName : session.getAttributeNames()) {
@@ -115,6 +119,20 @@ public final class MapSession implements Session, Serializable {
return this.id;
}
String getOriginalId() {
return this.originalId;
}
void setOriginalId(String originalId) {
this.originalId = originalId;
}
public String changeSessionId() {
String changedId = generateId();
setId(changedId);
return changedId;
}
public Instant getLastAccessedTime() {
return this.lastAccessedTime;
}
@@ -188,5 +206,9 @@ public final class MapSession implements Session, Serializable {
return this.id.hashCode();
}
private static String generateId() {
return UUID.randomUUID().toString();
}
private static final long serialVersionUID = 7160779239673823561L;
}

View File

@@ -37,7 +37,7 @@ import org.springframework.session.events.SessionExpiredEvent;
* @author Rob Winch
* @since 1.0
*/
public class MapSessionRepository implements SessionRepository<Session> {
public class MapSessionRepository implements SessionRepository<MapSession> {
/**
* If non-null, this value is used to override
* {@link Session#setMaxInactiveInterval(Duration)}.
@@ -76,11 +76,15 @@ public class MapSessionRepository implements SessionRepository<Session> {
this.defaultMaxInactiveInterval = Integer.valueOf(defaultMaxInactiveInterval);
}
public void save(Session session) {
public void save(MapSession session) {
if (!session.getId().equals(session.getOriginalId())) {
this.sessions.remove(session.getOriginalId());
session.setOriginalId(session.getId());
}
this.sessions.put(session.getId(), new MapSession(session));
}
public Session findById(String id) {
public MapSession findById(String id) {
Session saved = this.sessions.get(id);
if (saved == null) {
return null;
@@ -96,8 +100,8 @@ public class MapSessionRepository implements SessionRepository<Session> {
this.sessions.remove(id);
}
public Session createSession() {
Session result = new MapSession();
public MapSession createSession() {
MapSession result = new MapSession();
if (this.defaultMaxInactiveInterval != null) {
result.setMaxInactiveInterval(
Duration.ofSeconds(this.defaultMaxInactiveInterval));

View File

@@ -37,6 +37,12 @@ public interface Session {
*/
String getId();
/**
* 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
*/
String changeSessionId();
/**
* Gets the Object associated with the specified name or null if no Object is
* associated to that name.

View File

@@ -18,9 +18,6 @@ package org.springframework.session.web.http;
import java.io.IOException;
import java.time.Instant;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
@@ -274,30 +271,7 @@ public class SessionRepositoryFilter<S extends Session>
"Cannot change session ID. There is no session associated with this request.");
}
// eagerly get session attributes in case implementation lazily loads them
Map<String, Object> attrs = new HashMap<>();
Enumeration<String> iAttrNames = session.getAttributeNames();
while (iAttrNames.hasMoreElements()) {
String attrName = iAttrNames.nextElement();
Object value = session.getAttribute(attrName);
attrs.put(attrName, value);
}
SessionRepositoryFilter.this.sessionRepository.deleteById(session.getId());
HttpSessionWrapper original = getCurrentSession();
setCurrentSession(null);
HttpSessionWrapper newSession = getSession();
original.setSession(newSession.getSession());
newSession.setMaxInactiveInterval(session.getMaxInactiveInterval());
for (Map.Entry<String, Object> attr : attrs.entrySet()) {
String attrName = attr.getKey();
Object attrValue = attr.getValue();
newSession.setAttribute(attrName, attrValue);
}
return newSession.getId();
return getCurrentSession().getSession().changeSessionId();
}
@Override

View File

@@ -145,4 +145,32 @@ public class MapReactorSessionRepositoryTests {
assertThat(session.getMaxInactiveInterval())
.isEqualTo(expectedMaxInterval);
}
@Test
public void changeSessionIdWhenNotYetSaved() {
MapSession createSession = this.repository.createSession().block();
String originalId = createSession.getId();
createSession.changeSessionId();
this.repository.save(createSession).block();
assertThat(this.repository.findById(originalId).block()).isNull();
assertThat(this.repository.findById(createSession.getId()).block()).isNotNull();
}
@Test
public void changeSessionIdWhenSaved() {
MapSession createSession = this.repository.createSession().block();
this.repository.save(createSession).block();
String originalId = createSession.getId();
createSession.changeSessionId();
this.repository.save(createSession).block();
assertThat(this.repository.findById(originalId).block()).isNull();
assertThat(this.repository.findById(createSession.getId()).block()).isNotNull();
}
}

View File

@@ -66,4 +66,32 @@ public class MapSessionRepositoryTests {
assertThat(session.getMaxInactiveInterval())
.isEqualTo(expectedMaxInterval);
}
@Test
public void changeSessionIdWhenNotYetSaved() {
MapSession createSession = this.repository.createSession();
String originalId = createSession.getId();
createSession.changeSessionId();
this.repository.save(createSession);
assertThat(this.repository.findById(originalId)).isNull();
assertThat(this.repository.findById(createSession.getId())).isNotNull();
}
@Test
public void changeSessionIdWhenSaved() {
MapSession createSession = this.repository.createSession();
this.repository.save(createSession);
String originalId = createSession.getId();
createSession.changeSessionId();
this.repository.save(createSession);
assertThat(this.repository.findById(originalId)).isNull();
assertThat(this.repository.findById(createSession.getId())).isNotNull();
}
}

View File

@@ -134,6 +134,10 @@ public class MapSessionTests {
return Instant.EPOCH;
}
public String changeSessionId() {
throw new UnsupportedOperationException();
}
public String getId() {
return "id";
}

View File

@@ -76,9 +76,9 @@ public class SessionRepositoryFilterTests {
private Map<String, Session> sessions;
private SessionRepository<Session> sessionRepository;
private SessionRepository<MapSession> sessionRepository;
private SessionRepositoryFilter<Session> filter;
private SessionRepositoryFilter<MapSession> filter;
private MockHttpServletRequest request;
@@ -422,7 +422,7 @@ public class SessionRepositoryFilterTests {
public void doFilterSetsCookieIfChanged() throws Exception {
this.sessionRepository = new MapSessionRepository() {
@Override
public Session findById(String id) {
public MapSession findById(String id) {
return createSession();
}
};
@@ -1256,7 +1256,7 @@ public class SessionRepositoryFilterTests {
@SuppressWarnings("unchecked")
public void doFilterRequestSessionNoRequestSessionNoSessionRepositoryInteractions()
throws Exception {
SessionRepository<Session> sessionRepository = spy(new MapSessionRepository());
SessionRepository<MapSession> sessionRepository = spy(new MapSessionRepository());
this.filter = new SessionRepositoryFilter<>(sessionRepository);
@@ -1283,7 +1283,7 @@ public class SessionRepositoryFilterTests {
@Test
public void doFilterLazySessionCreation() throws Exception {
SessionRepository<Session> sessionRepository = spy(new MapSessionRepository());
SessionRepository<MapSession> sessionRepository = spy(new MapSessionRepository());
this.filter = new SessionRepositoryFilter<>(sessionRepository);
@@ -1299,9 +1299,9 @@ public class SessionRepositoryFilterTests {
@Test
public void doFilterLazySessionUpdates() throws Exception {
Session session = this.sessionRepository.createSession();
MapSession session = this.sessionRepository.createSession();
this.sessionRepository.save(session);
SessionRepository<Session> sessionRepository = spy(this.sessionRepository);
SessionRepository<MapSession> sessionRepository = spy(this.sessionRepository);
setSessionCookie(session.getId());
this.filter = new SessionRepositoryFilter<>(sessionRepository);