目錄
- 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自身對檢驗