Skip to content

Session ID generation strategy #1547

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
* <p>
Expand All @@ -44,6 +43,7 @@
*
* @author Rob Winch
* @author Vedran Pavic
* @author Jakub Maciej
* @since 1.0
*/
public final class MapSession implements Session, Serializable {
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -34,6 +35,7 @@
* </p>
*
* @author Rob Winch
* @author Jakub Maciej
* @since 1.0
*/
public class MapSessionRepository implements SessionRepository<MapSession> {
Expand All @@ -46,6 +48,8 @@ public class MapSessionRepository implements SessionRepository<MapSession> {

private final Map<String, Session> 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}.
Expand Down Expand Up @@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -36,10 +37,13 @@
* </p>
*
* @author Rob Winch
* @author Jakub Maciej
* @since 2.0
*/
public class ReactiveMapSessionRepository implements ReactiveSessionRepository<MapSession> {

private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault();

/**
* If non-null, this value is used to override
* {@link Session#setMaxInactiveInterval(Duration)}.
Expand Down Expand Up @@ -98,12 +102,22 @@ public Mono<Void> deleteById(String id) {
@Override
public Mono<MapSession> 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));
}
return Mono.just(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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
*
* @author Rob Winch
* @author Vedran Pavic
* @author Jakub Maciej
* @since 1.0
*/
public interface Session {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*
* @param <S> the {@link Session} type
* @author Rob Winch
* @author Jakub Maciej
* @since 1.0
*/
public interface SessionRepository<S extends Session> {
Expand Down Expand Up @@ -68,4 +69,11 @@ public interface SessionRepository<S extends Session> {
*/
void deleteById(String id);

/**
* Changes the session id.
* @param session which id should be changed
* @return new session id
*/
String changeSessionId(S session);

}
Original file line number Diff line number Diff line change
@@ -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();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
* @author Rob Winch
* @author Vedran Pavic
* @author Josh Cummings
* @author Jakub Maciej
*/
@Order(SessionRepositoryFilter.DEFAULT_ORDER)
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,12 +48,15 @@
* @param <S> the {@link Session} type
* @author Rob Winch
* @author Vedran Pavic
* @author Jakub Maciej
* @since 2.0
*/
public class SpringSessionWebSessionStore<S extends Session> implements WebSessionStore {

private final ReactiveSessionRepository<S> sessions;

private SessionIdStrategy sessionIdStrategy = SessionIdStrategy.getDefault();

private Clock clock = Clock.system(ZoneOffset.UTC);

public SpringSessionWebSessionStore(ReactiveSessionRepository<S> reactiveSessionRepository) {
Expand Down Expand Up @@ -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}.
*/
Expand All @@ -134,7 +148,7 @@ public String getId() {
@Override
public Mono<Void> changeSessionId() {
return Mono.defer(() -> {
this.session.changeSessionId();
this.session.changeSessionId(SpringSessionWebSessionStore.this.sessionIdStrategy.createSessionId());
return save();
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> indexes = this.indexResolver.resolveIndexesFor(session);
Expand Down
Loading