Manually expire web sessions

Instead of relying on the cache implementation to kill web sessions
past the time the admin has configured as allowed, track the end time
when the session was created and initially stored in the cache. If a
valid session key is found in the cache, but it expired before now,
invalidate the key and report it as not found.

Sessions are usually updated in the cache every 50% of the maxAge,
rotating the key and changing the cookie during this time. During one
of these half-expired rotations the session will have a new expire
time set, keeping it alive for a longer time window since the user
is still active.

This saves the cache from needing to update the on access timer on
every request, especially for disk based caches that try to save the
web_sessions across server restarts.

While we are poking at this cache, use String as the cache key rather
than Key now that the H2 based system doesn't need to use the key for
version checks. This simplifies the BloomFilter logic inside of the
disk based cache to be able to hash the token more quickly.

Change-Id: I318e38b2382b7f5ea1188df3ddc7ec6703a5fd3c
This commit is contained in:
Shawn O. Pearce
2012-05-16 17:53:16 -07:00
parent cfd994548e
commit 35303ed6a6
2 changed files with 26 additions and 5 deletions

View File

@@ -170,7 +170,7 @@ public final class CacheBasedWebSession implements WebSession {
/** Set the user account for this current request only. */
public void setUserAccountId(Account.Id id) {
key = new Key("id:" + id);
val = new Val(id, 0, false, null, "");
val = new Val(id, 0, false, null, "", 0);
}
public void logout() {

View File

@@ -43,6 +43,7 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.concurrent.TimeUnit;
@Singleton
class WebSessionManager {
@@ -104,7 +105,9 @@ class WebSessionManager {
final long halfAgeRefresh = sessionMaxAgeMillis >>> 1;
final long minRefresh = MILLISECONDS.convert(1, HOURS);
final long refresh = Math.min(halfAgeRefresh, minRefresh);
final long refreshCookieAt = now() + refresh;
final long now = now();
final long refreshCookieAt = now + refresh;
final long expiresAt = now + sessionMaxAgeMillis;
if (xsrfToken == null) {
// If we don't yet have a token for this session, establish one.
@@ -115,7 +118,8 @@ class WebSessionManager {
xsrfToken = CookieBase64.encode(rnd);
}
Val val = new Val(who, refreshCookieAt, remember, lastLogin, xsrfToken);
Val val = new Val(who, refreshCookieAt, remember,
lastLogin, xsrfToken, expiresAt);
self.put(key, val);
return val;
}
@@ -137,7 +141,12 @@ class WebSessionManager {
}
Val get(final Key key) {
return self.get(key);
Val val = self.get(key);
if (val != null && val.expiresAt <= now()) {
self.remove(key);
return null;
}
return val;
}
void destroy(final Key key) {
@@ -184,15 +193,18 @@ class WebSessionManager {
private transient boolean persistentCookie;
private transient AccountExternalId.Key externalId;
private transient String xsrfToken;
private transient long expiresAt;
Val(final Account.Id accountId, final long refreshCookieAt,
final boolean persistentCookie, final AccountExternalId.Key externalId,
final String xsrfToken) {
final String xsrfToken,
final long expiresAt) {
this.accountId = accountId;
this.refreshCookieAt = refreshCookieAt;
this.persistentCookie = persistentCookie;
this.externalId = externalId;
this.xsrfToken = xsrfToken;
this.expiresAt = expiresAt;
}
Account.Id getAccountId() {
@@ -233,6 +245,9 @@ class WebSessionManager {
writeVarInt32(out, 5);
writeString(out, xsrfToken);
writeVarInt32(out, 6);
writeFixInt64(out, expiresAt);
writeVarInt32(out, 0);
}
@@ -257,10 +272,16 @@ class WebSessionManager {
case 5:
xsrfToken = readString(in);
continue;
case 6:
expiresAt = readFixInt64(in);
continue;
default:
throw new IOException("Unknown tag found in object: " + tag);
}
}
if (expiresAt == 0) {
expiresAt = refreshCookieAt + TimeUnit.HOURS.toMillis(2);
}
}
}
}