cc.protea.platform.SessionUtilRedis Maven / Gradle / Ivy
package cc.protea.platform;
import cc.protea.foundation.integrations.RedisUtil;
import cc.protea.foundation.integrations.RedisUtil.WithJedis;
import cc.protea.foundation.integrations.RedisUtil.WithJedisNull;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import java.util.Set;
/**
* Within Redis the following areas are maintained:
*
* sessions - zset - session expiration date in ms; MAX if session does not expire
* sessions:TOKEN - hash - { userId, expireSeconds }
* users:sessions - zset - userId is the score, session token is the value
*/
class SessionUtilRedis {
private static final Integer TTL_SECONDS = 60 * 60 * 24 * 30; // One month
/**
* Searches for a user associated with the token. If one is found user has a session and is authenticated.
* Will automatically retry one time if there's any issue. Any further exceptions are thrown upstream.
* @param token
* @return Authenticated user id
*/
static Long getUserId(final String token) {
try {
return getUserIdInternal(token);
} catch (Exception e) {
return getUserIdInternal(token);
}
}
static Long getUserIdInternal(final String token) {
return RedisUtil.execute(new WithJedis() { public Long process(final Jedis jedis) {
// fetch the user for this session
String userString = jedis.get("sessions:active:" + StringUtils.trim(token));
if (! StringUtils.isNumeric(userString)) {
return null;
}
// refresh the expiry
jedis.expireAt("sessions:" + StringUtils.trim(token), TTL_SECONDS);
//jedis.zadd("sessions", new Date().getTime(), StringUtils.trim(token));
return NumberUtils.createLong(userString);
}});
}
static void create(final Long userId, final String token) {
RedisUtil.jedis(new WithJedisNull() { public void process(final Jedis jedis) {
Transaction t = jedis.multi();
t.setex("sessions:active:" + token, TTL_SECONDS, userId.toString()); // Add sessions: key with ttl
t.zadd("sessions", Double.MAX_VALUE, token); // Add token to bottom of the sessions sorted set (Do we need this?)
t.zadd("users:sessions", userId, token); // associate token and userid in a way we can easily look up
t.exec();
}});
}
static void remove(final String token) {
if (token == null) {
return;
}
final String trimmed = StringUtils.trim(token);
RedisUtil.jedis(new WithJedisNull() { public void process(final Jedis jedis) {
Transaction t = jedis.multi();
t.zrem("sessions", trimmed);
t.del("sessions:active:" + trimmed);
t.zrem("users:sessions", trimmed);
t.exec();
}});
}
static void removeAllForUser(final Long userId) {
Set sessions = RedisUtil.execute(new WithJedis>() { public Set process(final Jedis jedis) {
return jedis.zrangeByScore("users:sessions", userId, userId);
}});
for (String session : sessions) {
SessionUtil.remove(session);
}
}
static Set getAllForUser(final Long userId) {
if (userId == null) {
return null;
}
return RedisUtil.execute(new WithJedis>() { public Set process(final Jedis jedis) {
return jedis.zrangeByScore("users:sessions", userId, userId);
}});
}
}