shiro是如何清除过期session的(源码版本shiro1.6)
目录
- shiro是如何清除过期session的(源码版本shiro1.6)
- 一、SecurityManager
- 一、session的创建
- 二 session的定时清除
- 其他-登出时时候如何删除session的
- 其他-session最后操作时间如何更新
- 总结:
shiro是如何清除过期session的(源码版本shiro1.6)
- 20201211 Vic.xu
- 文章来源:临窗旋墨的博客,转载需注明出处
由于项目中的shiro的缓存管理器替换为redis,故此处简略追踪记录过期session的销毁过程。
一、SecurityManager
安全管理器SecurityManager可以说是shiro最最重要的组件
- 所有的安全交互都和它相关
- 管理着所有的Subject
- CacheManager交由它管理
- Realm也交由它管理
- SessionManager也由它管理
一、session的创建
session的创建的入口是SessionsSecurityManager#start,它进而调用SessionManager的start方法
public Session start(SessionContext context) throws AuthorizationException {
return this.sessionManager.start(context);
}
AbstractNativeSessionManager#start创建session方法如下
public Session start(SessionContext context) {
// 创建session:SimpleSession
Session session = createSession(context);
//设置session的timeout时间:默认30分钟
applyGlobalSessionTimeout(session);
//根据sessionId 生成cookie存入到request response中
onStart(session, context);
//通知session监听器
notifyStart(session);
//Don't expose the EIS-tier Session object to the client-tier:
//把SimpleSession包装为DelegatingSession
return createExposedSession(session, context);
}
AbstractValidatingSessionManager#createSession
protected Session createSession(SessionContext context) throws AuthorizationException {
enableSessionValidationIfNecessary();
return doCreateSession(context);
}
doCreateSession方法简要说明
- 通过SimpleSessionFactory创建session实例:SimpleSession
- 通过SessionDAO缓存session;
- 默认MemorySessionDAO存储在内存中的ConcurrentMap
- 根据配置的缓存管理器,缓存session,我们的项目使用的是EnterpriseCacheSessionDAO,并为此dao配置了自定义的cacheManager(基于redis的)
二 session的定时清除
在上文创建session的时候首选执行的是方法是enableSessionValidationIfNecessary,它开启了一个定时器
AbstractValidatingSessionManager#enableSessionValidationIfNecessary方法说明
private void enableSessionValidationIfNecessary() {
//获取session校验调度器
SessionValidationScheduler scheduler = getSessionValidationScheduler();
if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
//启用session检验
enableSessionValidation();
}
}
在追踪定时器源码之前,我们先看一下SimpleSession的基本属性有哪些
SimpleSession部分源码查看
public class SimpleSession implements ValidatingSession, Serializable {
//.....
//session Id
private transient Serializable id;
//session的创建时间
private transient Date startTimestamp;
//session的停止时间
private transient Date stopTimestamp;
//session的最近一次访问时间,初始值是startTimestamp
private transient Date lastAccessTime;
//session的有效时长,默认30分钟
private transient long timeout;
//session是否到期
private transient boolean expired;
//主机
private transient String host;
//存放的属性 session.setAttributes存入的属性
private transient Map<Object, Object> attributes;
//根据最后访问时间和有效时间判断 是否过期▲
protected boolean isTimedOut() {
//代码略
}
//更新最后访问时间 ▲
public void touch() {
this.lastAccessTime = new Date();
}
//session检验▲
public void validate() throws InvalidSessionException {
//check for stopped: stopTimestamp不为空 ▲
if (isStopped()) {
//timestamp is set, so the session is considered stopped:
throw new StoppedSessionException(msg);
}
//check for expiration 过期了 ▲
if (isTimedOut()) {
//设置 expired = true
expire();
//throw an exception explaining details of why it expired:
throw new ExpiredSessionException(msg);
}
}
}
了解了SimpleSession的基本结构我,我们继续查看session检验定时器
上文的enableSessionValidation方法进入到的是ExecutorServiceSessionValidationScheduler的enableSessionValidation方法
ExecutorServiceSessionValidationScheduler#enableSessionValidation
- 默认每隔一小时执行一次run方法
public void enableSessionValidation() {
// 创建ScheduledExecutorService
this.service = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
//.....
}
});
// 初始化service interval时长之后开始执行this的run方法,每隔interval执行一次;
this.service.scheduleAtFixedRate(this, interval, interval, TimeUnit.MILLISECONDS);
}
this.enabled = true;
}
ExecutorServiceSessionValidationScheduler#run
public void run() {
//....
this.sessionManager.validateSessions();
//....
}
- 进入AbstractValidatingSessionManager的validateSessions方法
AbstractValidatingSessionManager的validateSessions
public void validateSessions() {
//....
//获取sessionDao中的全部session
Collection<Session> activeSessions = getActiveSessions();
//分别校验每个session
for (Session s : activeSessions) {
SessionKey key = new DefaultSessionKey(s.getId());
//真正的校验方法
validate(s, key);
}
}
AbstractValidatingSessionManager#validate ▲▲▲
protected void validate(Session session, SessionKey key) throws InvalidSessionException {
try {
doValidate(session);
} catch (ExpiredSessionException ese) {
//从sessionDao中删除过期的session
onExpiration(session, ese, key);
throw ese;
} catch (InvalidSessionException ise) {
//从sessionDao中删除不合法的session
onInvalidation(session, ise, key);
throw ise;
}
}
-
doValidate方法 调用上文的SimpleSession中的validate进行校验,它会在过期以及已经停止的情况下抛出异常
if (session instanceof ValidatingSession) { ((ValidatingSession) session).validate(); } else { throw new IllegalStateException(msg); }
-
onExpiration方法从sessionDao中删除session▲▲▲
protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { onExpiration(s); notifyExpiration(s); afterExpired(s); }
afterExpired调用的是DefaultSessionManager的afterExpired方法
DefaultSessionManager#afterExpired:通过SessionDao中删除session
- 或者是从内存中直接删除session
- 或者调用缓存管理器的remove方法
protected void afterExpired(Session session) {
if (isDeleteInvalidSessions()) {
delete(session);
}
}
其他-登出时时候如何删除session的
- DelegatingSubject#logout →
- DefaultSecurityManager#logout →
- stopSession(subject);即调用SimpleSession的stop方法
其他-session最后操作时间如何更新
- 每次进入ShiroFilter都如自动调用
- ShiroFilter 的父类AbstractShiroFilter在执行doFilterInternal方法 的时候会调用updateSessionLastAccessTime方法,在其内部执行了 session.touch();
- AbstractShiroFilter的doFilterInternal调用时机在其父类OncePerRequestFilter中的doFilter方法内,它会调用doFilterInternal方法
其他-AbstractNativeSessionManager中的lookupSession中的session校验
- AbstractNativeSessionManager 对session的相关操作(如属性操作/设置过期时间/stop/touch等等等)均为调用lookupSession方法
- 在lookupSession方法中调用doGetSession方法。
- 调用的就是AbstractValidatingSessionManager 的validate方法,参见上文定时器作为入口时,对session的清除等处理
总结:
-
了解shiro中最重要的对象securityManager是一个大管家,它管理着shiro生命周期的几乎所有的关联对象。
-
sessionManager的相关管理了session相关的所有操作,包括对session检验的定时器的定义,同时在session其他的操作的时候也会检验session的有效性
-
SimpleSession自身对检验