您的位置:首页 > 数据库 > Redis

使用shiro和redis结合,管理SessionDAO的对Session的CRUD,并源码分析

2017-02-16 14:13 696 查看
SessionDAO的作用是为Session提供CRUD并进行持久化的一个shiro组件,将集成redis缓存进行开发

由配置文件可以知道sessionManager需要注入一个sessionDao

<!-- 自定义会话管理配置 -->
<bean id="sessionManager" class="com.dq.shiro.security.SessionManager">
<property name="sessionDAO" ref="sessionDAO"/>
<!-- 配置会话监听器 -->
<property name="sessionListeners" ref="mySessionListener" />
<!-- 会话超时时间,单位:毫秒 -->
<property name="globalSessionTimeout" value="${session.sessionTimeout}"/>

<!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话 -->
<property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
<!-- <property name="sessionValidationSchedulerEnabled" value="false"/> -->
<property name="sessionValidationSchedulerEnabled" value="true"/>

<property name="sessionIdCookie" ref="sessionIdCookie"/>
<property name="sessionIdCookieEnabled" value="true"/>
</bean>自定义的SessionManager的继承关系为
SessionManager extends DefaultWebSessionManager
DefaultWebSessionManager extends DefaultSessionManager我们能够在DefaultSessionManager中发现我们注入的SessionDAO
public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware {

//TODO - complete JavaDoc

private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class);

private SessionFactory sessionFactory;

protected SessionDAO sessionDAO; //todo - move SessionDAO up to AbstractValidatingSessionManager?

DefaultSessionManager所有对session的crud的操作都是由sessionDAO执行的,DefaultSessionManager中对应session操作的方法有
create,delete,update等,比如create

protected void create(Session session) {
if (log.isDebugEnabled()) {
log.debug("Creating new EIS record for new session instance [" + session + "]");
}
sessionDAO.create(session);
}

@Override
protected void onStop(Session session) {
if (session instanceof SimpleSession) {
SimpleSession ss = (SimpleSession) session;
Date stopTs = ss.getStopTimestamp();
ss.setLastAccessTime(stopTs);
}
onChange(session);
}
    @Override
    protected void afterStopped(Session session) {
        if (isDeleteInvalidSessions()) {
            delete(session);
        }
    }

    protected void onExpiration(Session session) {
        if (session instanceof SimpleSession) {
            ((SimpleSession) session).setExpired(true);
        }
        onChange(session);
    }
这样我们就理清了SessionManager和sessionDAO的关系,SessionManager注入sessionDAO,实现Session的CRUD
层级结构

ShiroFilterFactoryBean

-->securityManager

-->realm

-->cacheManager

-->rememberMeManager

-->sessionManager

-->sessionDAO
-->cacheManager

SessionDAO的配置

<!-- 自定义Session存储容器 -->
<bean id="sessionDAO" class="com.dq.shiro.security.CacheSessionDAO">
<property name="sessionIdGenerator" ref="sessionIdGen" />
<property name="activeSessionsCacheName" value="activeSessionsCache" />
<property name="cacheManager" ref="shiroCacheManager" />
</bean>
<!-- 定义授权缓存管理器 -->
<!-- <bean id="shiroCacheManager" class="com.minstone.common.security.shiro.cache.SessionCacheManager" // -->
<bean id="shiroCacheManager" class="com.dq.shiro.cache.RedisCacheManager">
<property name="redisManager" ref="redisManager" />
</bean>

可以看到sessionDAO需要注入sessionIdGenerator和cacheManager

而cacheManger需要注入一个redisManager,这个redisManager是一个直接操作redisApi的类
首先说一下sessionIdGenerator这是一个比较简单的内容,就是一个SessionId的自定义生成器

首先说下sessionDAO继承结构

CacheSessionDAO extends EnterpriseCacheSessionDAO
EnterpriseCacheSessionDAO extends CachingSessionDAO
CachingSessionDAO extends AbstractSessionDAO

在AbstractSessionDAO 发现了注入的SessionIdGenerator,其实这个引用就是为了生成一个我们自己需要的sessionId生成方式
public abstract class AbstractSessionDAO implements SessionDAO {

/**
* Optional SessionIdGenerator instance available to subclasses via the
* {@link #generateSessionId(org.apache.shiro.session.Session)} method.
*/
private SessionIdGenerator sessionIdGenerator;

再来看sessionIdGenerator的具体类SessionIdGen
public class SessionIdGen implements SessionIdGenerator {

/**
* @Description: TODO(Session ID 生成)
* @param: @param session
* @param: @return
* @throws
*/
@Override
public Serializable generateId(Session session) {
return IdGen.uuid();
}
}
在CachingSessionDAO发现了注入的cacheManager
public abstract class CachingSessionDAO extends AbstractSessionDAO implements CacheManagerAware {

/**
* The default active sessions cache name, equal to {@code shiro-activeSessionCache}.
*/
public static final String ACTIVE_SESSION_CACHE_NAME = "shiro-activeSessionCache";

/**
* The CacheManager to use to acquire the Session cache.
*/
private CacheManager cacheManager;

在CachingSessionDAO中发现了create方法,这个方法就是DefaultSessionManager的create方法中的sessionDAO创建session的实现,

这样也证实了SessionManager和sessionDAO的联系
//CachingSessionDAO 的create
public Serializable create(Session session) {
Serializable sessionId = super.create(session);
cache(session, sessionId);
return sessionId;
}
cache(session, sessionId);这行就是操作缓存的核心,继续跟踪源码
protected void cache(Session session, Serializable sessionId) {
if (session == null || sessionId == null) {
return;
}
Cache<Serializable, Session> cache = getActiveSessionsCacheLazy();
if (cache == null) {
return;
}
cache(session, sessionId, cache);
}

我们看到了获取cache的操作getActiveSessionsCacheLazy();这些方法都是CachingSessionDAO中的方法
private Cache<Serializable, Session> getActiveSessionsCacheLazy() {
if (this.activeSessions == null) {
this.activeSessions = createActiveSessionsCache();
}
return activeSessions;
}
protected Cache<Serializable, Session> createActiveSessionsCache() {
Cache<Serializable, Session> cache = null;
CacheManager mgr = getCacheManager();
if (mgr != null) {
String name = getActiveSessionsCacheName();
cache = mgr.getCache(name);
}
return cache;
}重点来了 CacheManager mgr = getCacheManager();获取到了我们在SessionDAO中注入的cacheManager

这也是RedisCacheManager implements CacheManager需要实现的方法
@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
logger.debug("获取名称为: " + name + " 的RedisCache实例");

Cache c = caches.get(name);

if (c == null) {

// initialize the Redis manager instance
redisManager.init();

// create a new cache instance
c = new RedisCache<K, V>(redisManager, keyPrefix);

// add it to the cache collection
caches.put(name, c);
}
return c;
}这里发现将redisManager当做RedisCache的key设置进来

这个RedisCache就是实现了Cache接口的实现类
public class RedisCache<K, V> implements Cache<K, V> {

/**
* The wrapped Jedis instance.
*/
private RedisManager cache;

/**
* The Redis key prefix for the sessions
*/
private String keyPrefix = "shiro_redis_session:";

/**
* Returns the Redis session keys prefix.
*
* @return The prefix
*/
public String getKeyPrefix() {
return keyPrefix;

这个redisCache通过RedisManager这个直接操作Jedis的类操作

所以操作就流程就是SessionManager(DefaultWebSessionManager )去引入SessionDAO,由SessionDAO操作缓存

SessionDAO注入cacheManager

cacheManager注入redisManager

在SessionDAO create delete update等方法时(被DefaultWebSessionManager调用)

会寻求cacheManager获取到一个实现了Cache接口的Cache对象(redisCache)

由这个对象来操作redis的CRUD(redisCache引入了redisManager,使用redisManager来操作redis)

自此 集成的源码分析完毕完毕
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: