If SessionRepositoryRequestWrapper.commitSession() is invoked twice
when a new session is created, then CookieHttpSessionStrategy will
add the same cookie twice. A couple examples of how this could happen:
* The response is committed and
SessionRepositoryResponseWrapper.onResponseCommitted() invokes
SessionRepositoryRequestWrapper.commitSession(). Then the finally
block in SessionRepositoryFilter invokes
SessionRepositoryRequestWrapper.commitSession() again.
* The new session is initialized and an Exception is thrown (i.e.
gh-229). The SessionRepositoryFilter invokes
SessionRepositoryRequestWrapper.commitSession() in the REQUEST
dispatch. Then in the ERROR dispatch SessionRepositoryFilter invokes
SessionRepositoryRequestWrapper.commitSession() invokes it again.
This commit ensures if the same Session is passed into
CookieHttpSessionStrategy multiple times within the same HttpServletRequest
it is only written once by keeping track of the sessions on a request
attribute.
Fixes gh-251
Previously, if the following happened:
* New Session Created
* Exception thrown
* Exception processed by error handler within Servlet
* Error Handler used a session
The result would be two sessions were created. This means the
data from the first session was also lost. This happend
because ERROR dispatch is a separate Filter invocation where
the request is no longer wrapped.
This commit ensures that currentSession is saved on a
HttpServletRequest attribute so that the ERROR dispatch sees
that a session was already created.
Fixes: gh-229
Previously, if a user had a reference to an existing HttpSession and
changed the session id, it would not work. For example:
HttpSession s = request.getSession();
request.changeSessionId();
s.setAttribute(...);
This commit fixes holding on to a reference of an HttpSession when
the session id is changed.
Fixes gh-227
Previously RedisHttpSessionConfiguration
enableRedisKeyspaceNotificationsInitializer return type was a package
protected class. This meant if someone extended
RedisHttpSessionConfiguration they got IllegalAccessErrors.
This changes the return type to InitializingBean.
Fixes gh-109
There's a duplicate of this field in
org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction which is
what is actually used.
Fixes gh-225
Previously forcibly cleaning up sessions was not working. All the cleanup
was done by Redis expiration. This meant that sessions would be kept alive
until Redis cleaned them up (non deterministic).
This commit resolves the mapping of expiration to session ids.
Fixes gh-169
This prevents the embedded redis from being picked up in ComponentScan
in any of the samples unless the @EnableEmebeddedRedis annotation is used.
Polish gh-184
The Problem:
The background task that cleans up sessions can incorrectly remove a
session due to a race condition.
Assume an existing session with the id "1" exists and will expire at
1420656360000. This means our redis store has the following:
spring:session:expirations:1420656360000 -> [1]
spring:session:session:1 -> <session>
Consider the following sequence:
* Thread 1 requests Session 1 and determines it should be forcibly deleted
up at 1420656420000
* Thread 2 requests Session 2 and determines it should be forcibly deleted
one minute later at 1420656480000
* Thread 2 removes Session 1 from 1420656360000, so it will no longer be
forcibly deleted at that time
spring:session:expirations:1420656360000 -> []
spring:session:session:1 -> <session>
* Thread 2 adds Session 1 to 1420656480000
spring:session:expirations:1420656360000 -> []
spring:session:session:1 -> <session>
spring:session:expirations:1420656480000 -> [1]
* Thread 1 removes Session 1 (which was already removed) from 1420656360000 (the original expiration)
spring:session:expirations:1420656360000 -> []
spring:session:session:1 -> <session>
spring:session:expirations:1420656480000 -> [1]
* Thread 1 adds Session 1 to 1420656420000
spring:session:expirations:1420656360000 -> []
spring:session:session:1 -> <session>
spring:session:expirations:1420656480000 -> [1]
spring:session:expirations:1420656420000 -> [1]
Now the session is mapped to be forcibly deleted in two locations.
However, at most it will be cleaned up in one location. This means that the
session will be forcibly deleted even if the session is continued to be
used.
Fixing the Issue:
Instead of deleting the session, we should have the background task access
the key which will only forcibly delete the key if it is expired. This mean
s that a session could at earliest be deleted when the value in the
datastore indicates.
This still means that a session can be deleted too soon since the
incorrect TTL may be set on a key. However, at worst this is the the
longest HTTP request length. Short of using distributed locking there
isn't a good answer to get exact consistency.
Fixes gh-93
Previously there was a possibility that Session to WebSocket mapping was
leaked if keyspace notifications were not enabled in Redis.
To resolve this the RedisHttpSessionConfiguration now ensures that Redis
is configured to enable Keyspace notifications.
Fixes gh-76 gh-81