All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.crazycake.shiro.RedisSessionDAO Maven / Gradle / Ivy

Go to download

shiro only provide the support of ehcache and concurrentHashMap. Here is an implement of redis cache can be used by shiro. Hope it will help you!

The newest version!
package org.crazycake.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.crazycake.shiro.common.SessionInMemory;
import org.crazycake.shiro.exception.SerializationException;
import org.crazycake.shiro.serializer.ObjectSerializer;
import org.crazycake.shiro.serializer.RedisSerializer;
import org.crazycake.shiro.serializer.StringSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;

/**
 * Used for setting/getting authentication information from Redis
 */
public class RedisSessionDAO extends AbstractSessionDAO {

	private static Logger logger = LoggerFactory.getLogger(RedisSessionDAO.class);

	private static final String DEFAULT_SESSION_KEY_PREFIX = "shiro:session:";
	private String keyPrefix = DEFAULT_SESSION_KEY_PREFIX;

	/**
	 * doReadSession be called about 10 times when login.
	 * Save Session in ThreadLocal to resolve this problem. sessionInMemoryTimeout is expiration of Session in ThreadLocal.
	 * The default value is 1000 milliseconds (1s).
	 * Most of time, you don't need to change it.
	 *
	 * You can turn it off by setting sessionInMemoryEnabled to false
	 */
	private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L;
	private long sessionInMemoryTimeout = DEFAULT_SESSION_IN_MEMORY_TIMEOUT;

	private static final boolean DEFAULT_SESSION_IN_MEMORY_ENABLED = true;
	private boolean sessionInMemoryEnabled = DEFAULT_SESSION_IN_MEMORY_ENABLED;

	private static ThreadLocal sessionsInThread = new ThreadLocal();

	/**
	 * expire time in seconds.
	 * NOTE: Please make sure expire is longer than session.getTimeout(),
	 * otherwise you might need the issue that session in Redis got erased when the Session is still available
	 *
	 * DEFAULT_EXPIRE: use the timeout of session instead of setting it by yourself
	 * NO_EXPIRE: never expire
	 */
	private static final int DEFAULT_EXPIRE = -2;
	private static final int NO_EXPIRE = -1;
	private int expire = DEFAULT_EXPIRE;

	private static final int MILLISECONDS_IN_A_SECOND = 1000;

	/**
	 * redisManager used for communicate with Redis
	 */
	private IRedisManager redisManager;

	/**
	 * Serializer of key
	 */
	private RedisSerializer keySerializer = new StringSerializer();

	/**
	 * Serializer of value
	 */
	private RedisSerializer valueSerializer = new ObjectSerializer();

	@Override
	public void update(Session session) throws UnknownSessionException {
		if (this.sessionInMemoryEnabled) {
			this.removeExpiredSessionInMemory();
		}
		this.saveSession(session);
		if (this.sessionInMemoryEnabled) {
			this.setSessionToThreadLocal(session.getId(), session);
		}
	}

	private void saveSession(Session session) throws UnknownSessionException {
		if (session == null || session.getId() == null) {
			logger.error("session or session id is null");
			throw new UnknownSessionException("session or session id is null");
		}
		byte[] key;
		byte[] value;
		try {
			key = keySerializer.serialize(getRedisSessionKey(session.getId()));
			value = valueSerializer.serialize(session);
		} catch (SerializationException e) {
			logger.error("serialize session error. session id=" + session.getId());
			throw new UnknownSessionException(e);
		}
		if (expire == DEFAULT_EXPIRE) {
			redisManager.set(key, value, (int) (session.getTimeout() / MILLISECONDS_IN_A_SECOND));
			return;
		}
		if (expire != NO_EXPIRE && expire * MILLISECONDS_IN_A_SECOND < session.getTimeout()) {
			logger.warn("Redis session expire time: "
					+ (expire * MILLISECONDS_IN_A_SECOND)
					+ " is less than Session timeout: "
					+ session.getTimeout()
					+ " . It may cause some problems.");
		}
		redisManager.set(key, value, expire);
	}

	@Override
	public void delete(Session session) {
		if (this.sessionInMemoryEnabled) {
			this.removeExpiredSessionInMemory();
		}
		if (session == null || session.getId() == null) {
			logger.error("session or session id is null");
			return;
		}
		if (this.sessionInMemoryEnabled) {
			this.delSessionFromThreadLocal(session.getId());
		}
		try {
			redisManager.del(keySerializer.serialize(getRedisSessionKey(session.getId())));
		} catch (SerializationException e) {
			logger.error("delete session error. session id=" + session.getId());
		}
	}

	@Override
	public Collection getActiveSessions() {
		if (this.sessionInMemoryEnabled) {
			this.removeExpiredSessionInMemory();
		}
		Set sessions = new HashSet();
		try {
			Set keys = redisManager.keys(keySerializer.serialize(this.keyPrefix + "*"));
			if (keys != null && keys.size() > 0) {
				for (byte[] key:keys) {
                    Object deserialize = valueSerializer.deserialize(redisManager.get(key));
                    if (deserialize == null) {
                        continue;
                    }
                    sessions.add((Session) deserialize);
				}
			}
		} catch (SerializationException e) {
			logger.error("get active sessions error.");
		}
		return sessions;
	}

	@Override
	protected Serializable doCreate(Session session) {
		if (this.sessionInMemoryEnabled) {
			this.removeExpiredSessionInMemory();
		}
		if (session == null) {
			logger.error("session is null");
			throw new UnknownSessionException("session is null");
		}
		Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        this.saveSession(session);
		return sessionId;
	}

	@Override
	protected Session doReadSession(Serializable sessionId) {
		if (this.sessionInMemoryEnabled) {
			this.removeExpiredSessionInMemory();
		}
		if (sessionId == null) {
			logger.warn("session id is null");
			return null;
		}
		if (this.sessionInMemoryEnabled) {
			Session session = getSessionFromThreadLocal(sessionId);
			if (session != null) {
				return session;
			}
		}
		Session session = null;
		try {
			String sessionRedisKey = getRedisSessionKey(sessionId);
			logger.debug("read session: " + sessionRedisKey + " from Redis");
			session = (Session) valueSerializer.deserialize(redisManager.get(keySerializer.serialize(sessionRedisKey)));
			if (this.sessionInMemoryEnabled) {
				setSessionToThreadLocal(sessionId, session);
			}
		} catch (SerializationException e) {
			logger.error("read session error. sessionId: " + sessionId);
		}
		return session;
	}

	private void setSessionToThreadLocal(Serializable sessionId, Session session) {
		this.initSessionsInThread();
		Map sessionMap = (Map) sessionsInThread.get();
		sessionMap.put(sessionId, this.createSessionInMemory(session));
	}

	private void delSessionFromThreadLocal(Serializable sessionId) {
		Map sessionMap = (Map) sessionsInThread.get();
		if (sessionMap == null) {
			return;
		}
		sessionMap.remove(sessionId);
	}

	private SessionInMemory createSessionInMemory(Session session) {
		SessionInMemory sessionInMemory = new SessionInMemory();
		sessionInMemory.setCreateTime(new Date());
		sessionInMemory.setSession(session);
		return sessionInMemory;
	}

	private void initSessionsInThread() {
		Map sessionMap = (Map) sessionsInThread.get();
		if (sessionMap == null) {
			sessionMap = new HashMap();
			sessionsInThread.set(sessionMap);
		}
	}

	private void removeExpiredSessionInMemory() {
		Map sessionMap = (Map) sessionsInThread.get();
		if (sessionMap == null) {
			return;
		}
		Iterator it = sessionMap.keySet().iterator();
		while (it.hasNext()) {
			Serializable sessionId = it.next();
			SessionInMemory sessionInMemory = sessionMap.get(sessionId);
			if (sessionInMemory == null) {
				it.remove();
				continue;
			}
			long liveTime = getSessionInMemoryLiveTime(sessionInMemory);
			if (liveTime > sessionInMemoryTimeout) {
				it.remove();
			}
		}
		if (sessionMap.size() == 0) {
			sessionsInThread.remove();
		}
	}

	private Session getSessionFromThreadLocal(Serializable sessionId) {
		if (sessionsInThread.get() == null) {
			return null;
		}

		Map sessionMap = (Map) sessionsInThread.get();
		SessionInMemory sessionInMemory = sessionMap.get(sessionId);
		if (sessionInMemory == null) {
			return null;
		}

		logger.debug("read session from memory");
		return sessionInMemory.getSession();
	}

	private long getSessionInMemoryLiveTime(SessionInMemory sessionInMemory) {
		Date now = new Date();
		return now.getTime() - sessionInMemory.getCreateTime().getTime();
	}

	private String getRedisSessionKey(Serializable sessionId) {
		return this.keyPrefix + sessionId;
	}

	public IRedisManager getRedisManager() {
		return redisManager;
	}

	public void setRedisManager(IRedisManager redisManager) {
		this.redisManager = redisManager;
	}

	public String getKeyPrefix() {
		return keyPrefix;
	}

	public void setKeyPrefix(String keyPrefix) {
		this.keyPrefix = keyPrefix;
	}

	public RedisSerializer getKeySerializer() {
		return keySerializer;
	}

	public void setKeySerializer(RedisSerializer keySerializer) {
		this.keySerializer = keySerializer;
	}

	public RedisSerializer getValueSerializer() {
		return valueSerializer;
	}

	public void setValueSerializer(RedisSerializer valueSerializer) {
		this.valueSerializer = valueSerializer;
	}

	public long getSessionInMemoryTimeout() {
		return sessionInMemoryTimeout;
	}

	public void setSessionInMemoryTimeout(long sessionInMemoryTimeout) {
		this.sessionInMemoryTimeout = sessionInMemoryTimeout;
	}

	public int getExpire() {
		return expire;
	}

	public void setExpire(int expire) {
		this.expire = expire;
	}

	public boolean getSessionInMemoryEnabled() {
		return sessionInMemoryEnabled;
	}

	public void setSessionInMemoryEnabled(boolean sessionInMemoryEnabled) {
		this.sessionInMemoryEnabled = sessionInMemoryEnabled;
	}

	public static ThreadLocal getSessionsInThread() {
		return sessionsInThread;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy