diff --git a/spring-session/src/main/java/org/springframework/session/MapSession.java b/spring-session/src/main/java/org/springframework/session/MapSession.java
index 34d899c5..9fd3c496 100644
--- a/spring-session/src/main/java/org/springframework/session/MapSession.java
+++ b/spring-session/src/main/java/org/springframework/session/MapSession.java
@@ -37,6 +37,7 @@ import java.util.UUID;
* This implementation has no synchronization, so it is best to use the copy constructor when working on multiple threads.
*
*
+ * @since 1.0
* @author Rob Winch
*/
public final class MapSession implements Session {
diff --git a/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java b/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java
index f8705aa2..a0a28b7a 100644
--- a/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java
+++ b/spring-session/src/main/java/org/springframework/session/MapSessionRepository.java
@@ -26,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
* distributed maps provided by NoSQL stores like Redis and Hazelcast.
*
* @author Rob Winch
- * @since 4.0
+ * @since 1.0
*/
public class MapSessionRepository implements SessionRepository {
private final Map sessions;
diff --git a/spring-session/src/main/java/org/springframework/session/Session.java b/spring-session/src/main/java/org/springframework/session/Session.java
index 9c661e34..08e3564d 100644
--- a/spring-session/src/main/java/org/springframework/session/Session.java
+++ b/spring-session/src/main/java/org/springframework/session/Session.java
@@ -23,7 +23,7 @@ import java.util.Set;
* Session, or even non web related sessions.
*
* @author Rob Winch
- * @since 4.0
+ * @since 1.0
*/
public interface Session extends Serializable {
/**
diff --git a/spring-session/src/main/java/org/springframework/session/SessionRepository.java b/spring-session/src/main/java/org/springframework/session/SessionRepository.java
index 04d4e210..28bb97c1 100644
--- a/spring-session/src/main/java/org/springframework/session/SessionRepository.java
+++ b/spring-session/src/main/java/org/springframework/session/SessionRepository.java
@@ -19,7 +19,7 @@ package org.springframework.session;
* A repository interface for managing {@link Session} instances.
*
* @author Rob Winch
- * @since 4.0
+ * @since 1.0
*/
public interface SessionRepository {
/**
diff --git a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
index b50d0439..e949cb41 100644
--- a/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
+++ b/spring-session/src/main/java/org/springframework/session/data/redis/RedisOperationsSessionRepository.java
@@ -20,6 +20,7 @@ import org.springframework.data.redis.core.RedisOperations;
import org.springframework.session.MapSession;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
+import org.springframework.util.Assert;
import java.util.HashMap;
import java.util.Map;
@@ -27,35 +28,120 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
- * A {@link org.springframework.session.SessionRepository} that is implemented using Spring Data's {@link org.springframework.data.redis.core.RedisOperations}. In a web environment, this is typically used in combination with
- * {@link org.springframework.session.web.SessionRepositoryFilter}.
+ *
+ * A {@link org.springframework.session.SessionRepository} that is implemented using Spring Data's
+ * {@link org.springframework.data.redis.core.RedisOperations}. In a web environment, this is typically used in
+ * combination with {@link org.springframework.session.web.SessionRepositoryFilter}.
+ *
*
+ *
Creating a new instance
+ *
+ * A typical example of how to create a new instance can be seen below:
+ *
+ *
+ * For additional information on how to create a RedisTemplate, refer to the
+ * Spring Data Redis Reference.
+ *
+ *
+ *
Storage Details
+ *
+ *
+ * Each session is stored in Redis as a Hash. Each session is
+ * set and updated using the HMSET command. An example of how each session
+ * is stored can be seen below.
+ *
+ * An expiration is associated to each session using the EXPIRE command based upon the
+ * {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#getMaxInactiveInterval()}.
+ * For example:
+ *
+ *
+ *
+ * EXPIRE spring-security-sessions: 1800
+ *
+ *
+ *
+ * The {@link RedisSession} keeps track of the properties that have changed and only updates those. This means if an attribute
+ * is written once and read many times we only need to write that attribute once. For example, assume the session attribute
+ * "sessionAttr2" from earlier was updated. The following would be executed upon saving:
+ *
*
* @since 1.0
*
* @author Rob Winch
*/
public class RedisOperationsSessionRepository implements SessionRepository {
+ /**
+ * The prefix for each key of the Redis Hash representing a single session. The suffix is the unique session id.
+ */
private final String BOUNDED_HASH_KEY_PREFIX = "spring-security-sessions:";
+
+ /**
+ * The key in the Hash representing {@link org.springframework.session.Session#getCreationTime()}
+ */
private final String CREATION_TIME_ATTR = "creationTime";
+
+ /**
+ * The key in the Hash representing {@link org.springframework.session.Session#getMaxInactiveInterval()}
+ */
private final String MAX_INACTIVE_ATTR = "maxInactiveInterval";
+
+ /**
+ * The key in the Hash representing {@link org.springframework.session.Session#getLastAccessedTime()}
+ */
private final String LAST_ACCESSED_ATTR = "lastAccessedTime";
+
+ /**
+ * The prefix of the key for used for session attributes. The suffix is the name of the session attribute. For
+ * example, if the session contained an attribute named attributeName, then there would be an entry in the hash named
+ * sessionAttr:attributeName that mapped to its value.
+ */
private final String SESSION_ATTR_PREFIX = "sessionAttr:";
+ private final RedisOperations redisOperations;
- private final RedisOperations redisTemplate;
-
+ /**
+ * If non-null, this value is used to override {@link RedisSession#setDefaultMaxInactiveInterval(int)}.
+ */
private Integer defaultMaxInactiveInterval;
- public RedisOperationsSessionRepository(RedisOperations redisTemplate) {
- this.redisTemplate = redisTemplate;
+ /**
+ * Creates a new instance. For an example, refer to the class level javadoc.
+ *
+ * @param redisOperations The {@link RedisOperations} to use. Cannot be null.
+ */
+ public RedisOperationsSessionRepository(RedisOperations redisOperations) {
+ Assert.notNull(redisOperations, "RedisOperations cannot be null");
+ this.redisOperations = redisOperations;
}
/**
* Sets the maximum inactive interval in seconds between requests before newly created sessions will be
- * invalidated. A negative time indicates that the session will never timeout.
+ * invalidated. A negative time indicates that the session will never timeout. The default is 1800 (30 minutes).
*
- * @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between client requests.
+ * @param defaultMaxInactiveInterval the number of seconds that the {@link Session} should be kept alive between
+ * client requests.
*/
public void setDefaultMaxInactiveInterval(int defaultMaxInactiveInterval) {
this.defaultMaxInactiveInterval = defaultMaxInactiveInterval;
@@ -68,7 +154,7 @@ public class RedisOperationsSessionRepository implements SessionRepository entries = getOperations(id).entries();
+ Map