Javadoc
This commit is contained in:
@@ -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.
|
||||
* </p>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class MapSession implements Session {
|
||||
|
||||
@@ -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<Session> {
|
||||
private final Map<String,Session> sessions;
|
||||
|
||||
@@ -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 {
|
||||
/**
|
||||
|
||||
@@ -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<S extends Session> {
|
||||
/**
|
||||
|
||||
@@ -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}.
|
||||
* <p>
|
||||
* 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}.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Creating a new instance</h2>
|
||||
*
|
||||
* A typical example of how to create a new instance can be seen below:
|
||||
*
|
||||
* <pre>
|
||||
* JedisConnectionFactory factory = new JedisConnectionFactory();
|
||||
*
|
||||
* RedisTemplate<String, Session> template = new RedisTemplate<String, Session>();
|
||||
* template.setKeySerializer(new StringRedisSerializer());
|
||||
* template.setHashKeySerializer(new StringRedisSerializer());
|
||||
* template.setConnectionFactory(factory);
|
||||
*
|
||||
* RedisOperationsSessionRepository redisSessionRepository = new RedisOperationsSessionRepository(template);
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For additional information on how to create a RedisTemplate, refer to the
|
||||
* <a href="http://docs.spring.io/spring-data/data-redis/docs/current/reference/html/">Spring Data Redis Reference</a>.
|
||||
* </p>
|
||||
*
|
||||
* <h2>Storage Details</h2>
|
||||
*
|
||||
* <p>
|
||||
* Each session is stored in Redis as a <a href="http://redis.io/topics/data-types#hashes">Hash</a>. Each session is
|
||||
* set and updated using the <a href="http://redis.io/commands/hmset">HMSET command</a>. An example of how each session
|
||||
* is stored can be seen below.
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring-security-sessions:<session-id> creationTime 1404360000000 maxInactiveInterval 1800 lastAccessedTime 1404360000000 sessionAttr:<attrName> someAttrValue sessionAttr2:<attrName> someAttrValue2
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* An expiration is associated to each session using the <a href="http://redis.io/commands/expire">EXPIRE command</a> based upon the
|
||||
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#getMaxInactiveInterval()}.
|
||||
* For example:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* EXPIRE spring-security-sessions:<session-id> 1800
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
* </p>
|
||||
*
|
||||
* <pre>
|
||||
* HMSET spring-security-sessions:<session-id> sessionAttr2:<attrName> newValue
|
||||
* EXPIRE spring-security-sessions:<session-id> 1800
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0
|
||||
*
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class RedisOperationsSessionRepository implements SessionRepository<RedisOperationsSessionRepository.RedisSession> {
|
||||
/**
|
||||
* 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<String,Session> redisOperations;
|
||||
|
||||
private final RedisOperations<String,Session> redisTemplate;
|
||||
|
||||
/**
|
||||
* If non-null, this value is used to override {@link RedisSession#setDefaultMaxInactiveInterval(int)}.
|
||||
*/
|
||||
private Integer defaultMaxInactiveInterval;
|
||||
|
||||
public RedisOperationsSessionRepository(RedisOperations<String, Session> 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<String, Session> 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<Redis
|
||||
|
||||
@Override
|
||||
public RedisSession getSession(String id) {
|
||||
Map<Object, Object> entries = getOperations(id).entries();
|
||||
Map<Object, Object> entries = getSessionBoundHashOperations(id).entries();
|
||||
if(entries.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
@@ -92,7 +178,7 @@ public class RedisOperationsSessionRepository implements SessionRepository<Redis
|
||||
@Override
|
||||
public void delete(String sessionId) {
|
||||
String key = getKey(sessionId);
|
||||
this.redisTemplate.delete(key);
|
||||
this.redisOperations.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -104,19 +190,42 @@ public class RedisOperationsSessionRepository implements SessionRepository<Redis
|
||||
return redisSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Hash key for this session by prefixing it appropriately.
|
||||
*
|
||||
* @param sessionId the session id
|
||||
* @return the Hash key for this session by prefixing it appropriately.
|
||||
*/
|
||||
private String getKey(String sessionId) {
|
||||
return BOUNDED_HASH_KEY_PREFIX + sessionId;
|
||||
}
|
||||
|
||||
private BoundHashOperations<String, Object, Object> getOperations(String sessionId) {
|
||||
/**
|
||||
* Gets the {@link BoundHashOperations} to operate on a {@link Session}
|
||||
* @param sessionId the id of the {@link Session} to work with
|
||||
* @return the {@link BoundHashOperations} to operate on a {@link Session}
|
||||
*/
|
||||
private BoundHashOperations<String, Object, Object> getSessionBoundHashOperations(String sessionId) {
|
||||
String key = getKey(sessionId);
|
||||
return this.redisTemplate.boundHashOps(key);
|
||||
return this.redisOperations.boundHashOps(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@link org.springframework.session.data.redis.RedisOperationsSessionRepository.RedisSession#saveDelta()} is invoked
|
||||
* all the attributes that have been changed will be persisted.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
class RedisSession implements Session {
|
||||
private final MapSession cached;
|
||||
private Map<String, Object> delta = new HashMap<String,Object>();
|
||||
|
||||
/**
|
||||
* Creates a new instance ensuring to mark all of the new attributes to be persisted in the next save operation.
|
||||
*/
|
||||
private RedisSession() {
|
||||
this(new MapSession());
|
||||
delta.put(CREATION_TIME_ATTR, getCreationTime());
|
||||
@@ -124,7 +233,13 @@ public class RedisOperationsSessionRepository implements SessionRepository<Redis
|
||||
delta.put(LAST_ACCESSED_ATTR, getLastAccessedTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance from the provided {@link MapSession}
|
||||
*
|
||||
* @param cached the {@MapSession} that represents the persisted session that was retrieved. Cannot be null.
|
||||
*/
|
||||
private RedisSession(MapSession cached) {
|
||||
Assert.notNull("MapSession cannot be null");
|
||||
this.cached = cached;
|
||||
}
|
||||
|
||||
@@ -182,10 +297,13 @@ public class RedisOperationsSessionRepository implements SessionRepository<Redis
|
||||
delta.put(SESSION_ATTR_PREFIX + attributeName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any attributes that have been changed and updates the expiration of this session.
|
||||
*/
|
||||
private void saveDelta() {
|
||||
getOperations(getId()).putAll(delta);
|
||||
getOperations(getId()).expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
getSessionBoundHashOperations(getId()).putAll(delta);
|
||||
getSessionBoundHashOperations(getId()).expire(getMaxInactiveInterval(), TimeUnit.SECONDS);
|
||||
delta.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,13 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* allow specifying a cookie name using {@link CookieHttpSessionStrategy#setCookieName(String)}. The default is "SESSION".
|
||||
*
|
||||
* When a session is created, the HTTP response will have a cookie with the specified cookie name and the value of the
|
||||
* session id. The cookie will be marked as a session cookie, marked as HTTPOnly, and if
|
||||
* {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the cookie will be marked as secure. For example:
|
||||
* session id. The cookie will be marked as a session cookie, use the context path for the path of the cookie, marked as
|
||||
* HTTPOnly, and if {@link javax.servlet.http.HttpServletRequest#isSecure()} returns true, the cookie will be marked as
|
||||
* secure. For example:
|
||||
*
|
||||
* <pre>
|
||||
* HTTP/1.1 200 OK
|
||||
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Secure; HttpOnly
|
||||
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Path=/context-root; Secure; HttpOnly
|
||||
* </pre>
|
||||
*
|
||||
* The client should now include the session in each request by specifying the same cookie in their request. For example:
|
||||
@@ -50,6 +51,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* Set-Cookie: SESSION=f81d4fae-7dec-11d0-a765-00a0c91e6bf6; Expires=Thur, 1 Jan 1970 00:00:00 GMT; Secure; HttpOnly
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public final class CookieHttpSessionStrategy implements HttpSessionStrategy {
|
||||
|
||||
@@ -47,6 +47,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
* x-auth-token:
|
||||
* </pre>
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class HeaderHttpSessionStrategy implements HttpSessionStrategy {
|
||||
|
||||
@@ -23,6 +23,7 @@ import javax.servlet.http.HttpServletResponse;
|
||||
/**
|
||||
* A strategy for mapping HTTP request and responses to a {@link Session}.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public interface HttpSessionStrategy {
|
||||
|
||||
@@ -26,6 +26,7 @@ import java.util.Locale;
|
||||
* Base class for response wrappers which encapsulate the logic for handling an event when the
|
||||
* {@link javax.servlet.http.HttpServletResponse} is committed.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
* Allows for easily ensuring that a request is only invoked once per request. This is a simplified version of spring-web's
|
||||
* OncePerRequestFilter and copied to reduce the foot print required to use the session support.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
abstract class OncePerRequestFilter implements Filter {
|
||||
|
||||
@@ -48,272 +48,292 @@ import java.util.Set;
|
||||
* <li>The client is notified that the session id is no longer valid with {@link HttpSessionStrategy#onInvalidateSession(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}</li>
|
||||
* </ul>
|
||||
*
|
||||
* session id is looked up using the provided {@link HttpSessionStrategy}. The same strategy is used to convey the
|
||||
* session id of newly created {@link org.springframework.session.Session}s to the client.
|
||||
*
|
||||
* @since 1.0
|
||||
* @author Rob Winch
|
||||
*/
|
||||
public class SessionRepositoryFilter<S extends Session> extends OncePerRequestFilter {
|
||||
private final SessionRepository<S> sessionRepository;
|
||||
private final SessionRepository<S> sessionRepository;
|
||||
|
||||
private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
private HttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
|
||||
|
||||
public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param sessionRepository the <code>SessionRepository</code> to use. Cannot be null.
|
||||
*/
|
||||
public SessionRepositoryFilter(SessionRepository<S> sessionRepository) {
|
||||
Assert.notNull(sessionRepository, "SessionRepository cannot be null");
|
||||
this.sessionRepository = sessionRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link HttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}.
|
||||
*
|
||||
* @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null.
|
||||
*/
|
||||
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
|
||||
Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy cannot be null");
|
||||
this.httpSessionStrategy = httpSessionStrategy;
|
||||
}
|
||||
/**
|
||||
* Sets the {@link HttpSessionStrategy} to be used. The default is a {@link CookieHttpSessionStrategy}.
|
||||
*
|
||||
* @param httpSessionStrategy the {@link HttpSessionStrategy} to use. Cannot be null.
|
||||
*/
|
||||
public void setHttpSessionStrategy(HttpSessionStrategy httpSessionStrategy) {
|
||||
Assert.notNull(httpSessionStrategy,"httpSessionIdStrategy cannot be null");
|
||||
this.httpSessionStrategy = httpSessionStrategy;
|
||||
}
|
||||
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response);
|
||||
try {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} finally {
|
||||
wrappedRequest.commitSession();
|
||||
}
|
||||
}
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
|
||||
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest,response);
|
||||
try {
|
||||
filterChain.doFilter(wrappedRequest, wrappedResponse);
|
||||
} finally {
|
||||
wrappedRequest.commitSession();
|
||||
}
|
||||
}
|
||||
|
||||
private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {
|
||||
/**
|
||||
* Allows ensuring that the session is saved if the response is committed.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class SessionRepositoryResponseWrapper extends OnCommittedResponseWrapper {
|
||||
|
||||
private final SessionRepositoryRequestWrapper request;
|
||||
private final SessionRepositoryRequestWrapper request;
|
||||
|
||||
/**
|
||||
* @param response the response to be wrapped
|
||||
*/
|
||||
public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {
|
||||
super(response);
|
||||
this.request = request;
|
||||
}
|
||||
/**
|
||||
* @param response the response to be wrapped
|
||||
*/
|
||||
public SessionRepositoryResponseWrapper(SessionRepositoryRequestWrapper request, HttpServletResponse response) {
|
||||
super(response);
|
||||
Assert.notNull(request, "SessionRepositoryRequestWrapper cannot be null");
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResponseCommitted() {
|
||||
request.commitSession();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected void onResponseCommitted() {
|
||||
request.commitSession();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link javax.servlet.http.HttpServletRequest} that retrieves the {@link javax.servlet.http.HttpSession} using a
|
||||
* {@link org.springframework.session.SessionRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 4.0
|
||||
*/
|
||||
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
|
||||
private HttpSessionWrapper currentSession;
|
||||
private boolean requestedValidSession;
|
||||
private final HttpServletResponse response;
|
||||
/**
|
||||
* A {@link javax.servlet.http.HttpServletRequest} that retrieves the {@link javax.servlet.http.HttpSession} using a
|
||||
* {@link org.springframework.session.SessionRepository}.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class SessionRepositoryRequestWrapper extends HttpServletRequestWrapper {
|
||||
private HttpSessionWrapper currentSession;
|
||||
private boolean requestedValidSession;
|
||||
private final HttpServletResponse response;
|
||||
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
|
||||
super(request);
|
||||
this.response = response;
|
||||
}
|
||||
private SessionRepositoryRequestWrapper(HttpServletRequest request, HttpServletResponse response) {
|
||||
super(request);
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
private void commitSession() {
|
||||
HttpSessionWrapper wrappedSession = currentSession;
|
||||
if(wrappedSession == null) {
|
||||
if(isInvalidateClientSession()) {
|
||||
httpSessionStrategy.onInvalidateSession(this, response);
|
||||
}
|
||||
} else {
|
||||
S session = wrappedSession.session;
|
||||
sessionRepository.save(session);
|
||||
httpSessionStrategy.onNewSession(session, this, response);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Uses the HttpSessionStrategy to write the session id tot he response and persist the Session.
|
||||
*/
|
||||
private void commitSession() {
|
||||
HttpSessionWrapper wrappedSession = currentSession;
|
||||
if(wrappedSession == null) {
|
||||
if(isInvalidateClientSession()) {
|
||||
httpSessionStrategy.onInvalidateSession(this, response);
|
||||
}
|
||||
} else {
|
||||
S session = wrappedSession.session;
|
||||
sessionRepository.save(session);
|
||||
httpSessionStrategy.onNewSession(session, this, response);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInvalidateClientSession() {
|
||||
return currentSession == null && requestedValidSession;
|
||||
}
|
||||
private boolean isInvalidateClientSession() {
|
||||
return currentSession == null && requestedValidSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession(boolean create) {
|
||||
if(currentSession != null) {
|
||||
return currentSession;
|
||||
}
|
||||
String requestedSessionId = getRequestedSessionId();
|
||||
if(requestedSessionId != null) {
|
||||
S session = sessionRepository.getSession(requestedSessionId);
|
||||
if(session != null) {
|
||||
this.requestedValidSession = true;
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
currentSession.setNew(false);
|
||||
return currentSession;
|
||||
}
|
||||
}
|
||||
if(!create) {
|
||||
return null;
|
||||
}
|
||||
S session = sessionRepository.createSession();
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
return currentSession;
|
||||
}
|
||||
@Override
|
||||
public HttpSession getSession(boolean create) {
|
||||
if(currentSession != null) {
|
||||
return currentSession;
|
||||
}
|
||||
String requestedSessionId = getRequestedSessionId();
|
||||
if(requestedSessionId != null) {
|
||||
S session = sessionRepository.getSession(requestedSessionId);
|
||||
if(session != null) {
|
||||
this.requestedValidSession = true;
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
currentSession.setNew(false);
|
||||
return currentSession;
|
||||
}
|
||||
}
|
||||
if(!create) {
|
||||
return null;
|
||||
}
|
||||
S session = sessionRepository.createSession();
|
||||
currentSession = new HttpSessionWrapper(session, getServletContext());
|
||||
return currentSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSession getSession() {
|
||||
return getSession(true);
|
||||
}
|
||||
@Override
|
||||
public HttpSession getSession() {
|
||||
return getSession(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRequestedSessionId() {
|
||||
return httpSessionStrategy.getRequestedSessionId(this);
|
||||
}
|
||||
@Override
|
||||
public String getRequestedSessionId() {
|
||||
return httpSessionStrategy.getRequestedSessionId(this);
|
||||
}
|
||||
|
||||
private final class HttpSessionWrapper implements HttpSession {
|
||||
final S session;
|
||||
private final ServletContext servletContext;
|
||||
private boolean invalidated;
|
||||
private boolean old;
|
||||
/**
|
||||
* Allows creating an HttpSession from a Session instance.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @since 1.0
|
||||
*/
|
||||
private final class HttpSessionWrapper implements HttpSession {
|
||||
final S session;
|
||||
private final ServletContext servletContext;
|
||||
private boolean invalidated;
|
||||
private boolean old;
|
||||
|
||||
public HttpSessionWrapper(S session, ServletContext servletContext) {
|
||||
this.session = session;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
public HttpSessionWrapper(S session, ServletContext servletContext) {
|
||||
this.session = session;
|
||||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
void updateLastAccessedTime() {
|
||||
checkState();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
}
|
||||
void updateLastAccessedTime() {
|
||||
checkState();
|
||||
session.setLastAccessedTime(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreationTime() {
|
||||
checkState();
|
||||
return session.getCreationTime();
|
||||
}
|
||||
@Override
|
||||
public long getCreationTime() {
|
||||
checkState();
|
||||
return session.getCreationTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return session.getId();
|
||||
}
|
||||
@Override
|
||||
public String getId() {
|
||||
return session.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastAccessedTime() {
|
||||
checkState();
|
||||
return session.getLastAccessedTime();
|
||||
}
|
||||
@Override
|
||||
public long getLastAccessedTime() {
|
||||
checkState();
|
||||
return session.getLastAccessedTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
session.setMaxInactiveInterval(interval);
|
||||
}
|
||||
@Override
|
||||
public void setMaxInactiveInterval(int interval) {
|
||||
session.setMaxInactiveInterval(interval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxInactiveInterval() {
|
||||
return session.getMaxInactiveInterval();
|
||||
}
|
||||
@Override
|
||||
public int getMaxInactiveInterval() {
|
||||
return session.getMaxInactiveInterval();
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpSessionContext getSessionContext() {
|
||||
return NOOP_SESSION_CONTEXT;
|
||||
}
|
||||
@Override
|
||||
public HttpSessionContext getSessionContext() {
|
||||
return NOOP_SESSION_CONTEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
checkState();
|
||||
return session.getAttribute(name);
|
||||
}
|
||||
@Override
|
||||
public Object getAttribute(String name) {
|
||||
checkState();
|
||||
return session.getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(String name) {
|
||||
return getAttribute(name);
|
||||
}
|
||||
@Override
|
||||
public Object getValue(String name) {
|
||||
return getAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
checkState();
|
||||
return Collections.enumeration(session.getAttributeNames());
|
||||
}
|
||||
@Override
|
||||
public Enumeration<String> getAttributeNames() {
|
||||
checkState();
|
||||
return Collections.enumeration(session.getAttributeNames());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getValueNames() {
|
||||
checkState();
|
||||
Set<String> attrs = session.getAttributeNames();
|
||||
return attrs.toArray(new String[0]);
|
||||
}
|
||||
@Override
|
||||
public String[] getValueNames() {
|
||||
checkState();
|
||||
Set<String> attrs = session.getAttributeNames();
|
||||
return attrs.toArray(new String[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkState();
|
||||
session.setAttribute(name, value);
|
||||
}
|
||||
@Override
|
||||
public void setAttribute(String name, Object value) {
|
||||
checkState();
|
||||
session.setAttribute(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putValue(String name, Object value) {
|
||||
setAttribute(name, value);
|
||||
}
|
||||
@Override
|
||||
public void putValue(String name, Object value) {
|
||||
setAttribute(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
checkState();
|
||||
session.removeAttribute(name);
|
||||
}
|
||||
@Override
|
||||
public void removeAttribute(String name) {
|
||||
checkState();
|
||||
session.removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeValue(String name) {
|
||||
removeAttribute(name);
|
||||
}
|
||||
@Override
|
||||
public void removeValue(String name) {
|
||||
removeAttribute(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void invalidate() {
|
||||
checkState();
|
||||
this.invalidated = true;
|
||||
currentSession = null;
|
||||
sessionRepository.delete(getId());
|
||||
}
|
||||
@Override
|
||||
public final void invalidate() {
|
||||
checkState();
|
||||
this.invalidated = true;
|
||||
currentSession = null;
|
||||
sessionRepository.delete(getId());
|
||||
}
|
||||
|
||||
public void setNew(boolean isNew) {
|
||||
this.old = !isNew;
|
||||
}
|
||||
public void setNew(boolean isNew) {
|
||||
this.old = !isNew;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
checkState();
|
||||
return !old;
|
||||
}
|
||||
@Override
|
||||
public boolean isNew() {
|
||||
checkState();
|
||||
return !old;
|
||||
}
|
||||
|
||||
private void checkState() {
|
||||
if(invalidated) {
|
||||
throw new IllegalStateException("The HttpSession has already be invalidated.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void checkState() {
|
||||
if(invalidated) {
|
||||
throw new IllegalStateException("The HttpSession has already be invalidated.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() {
|
||||
@Override
|
||||
public HttpSession getSession(String sessionId) {
|
||||
return null;
|
||||
}
|
||||
private static final HttpSessionContext NOOP_SESSION_CONTEXT = new HttpSessionContext() {
|
||||
@Override
|
||||
public HttpSession getSession(String sessionId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getIds() {
|
||||
return EMPTY_ENUMERATION;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public Enumeration<String> getIds() {
|
||||
return EMPTY_ENUMERATION;
|
||||
}
|
||||
};
|
||||
|
||||
private final static Enumeration<String> EMPTY_ENUMERATION = new Enumeration<String>() {
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
private final static Enumeration<String> EMPTY_ENUMERATION = new Enumeration<String>() {
|
||||
@Override
|
||||
public boolean hasMoreElements() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException("a");
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public String nextElement() {
|
||||
throw new NoSuchElementException("a");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user