Shiro 关于校验Session过期、有效性的设计概念
核心行为
开启Session校验调度任务、校验所有的session、具体的Session自我校验、关闭Session校验调度任务
核心类
ValidatingSessionManager、SessionValidationScheduler、ValidatingSession
ValidatingSessionManager的行为
public interface ValidatingSessionManager extends SessionManager { // 校验所有的Session void validateSessions(); }
SessionValidationScheduler的行为
public interface SessionValidationScheduler { // Session校验是否处于开启状态 boolean isEnabled(); // 开启Session校验 void enableSessionValidation(); // 关闭Session校验 void disableSessionValidation(); }
ValidatingSession的行为
public interface ValidatingSession extends Session { // 校验是否处于开启状态 boolean isValid(); // Session自我校验 void validate() throws InvalidSessionException; }
校验过程解析
ValidatingSessionManager开启Session校验调度任务 ==》 Session校验调度任务中使用ValidatingSessionManager校验所有的Session
==》 ValidatingSessionManager让其子类获得所有的Session(基础Session) ==》 各个基础Session自我校验
ValidatingSessionManager开启Session校验调度任务
protected SessionValidationScheduler sessionValidationScheduler; public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) { this.sessionValidationScheduler = sessionValidationScheduler; } protected synchronized void enableSessionValidation() { SessionValidationScheduler scheduler = getSessionValidationScheduler(); if (scheduler == null) { scheduler = createSessionValidationScheduler(); setSessionValidationScheduler(scheduler); } if (!scheduler.isEnabled()) { if (log.isInfoEnabled()) { log.info("Enabling session validation scheduler..."); } // 开启Session校验调度任务 scheduler.enableSessionValidation(); afterSessionValidationEnabled(); } }
Session校验调度任务中使用ValidatingSessionManager校验所有的Session
public void run() { if (log.isDebugEnabled()) { log.debug("Executing session validation..."); } long startTime = System.currentTimeMillis(); // Session校验任务调度时使用ValidatingSessionManager校验所有的Session this.sessionManager.validateSessions(); long stopTime = System.currentTimeMillis(); if (log.isDebugEnabled()) { log.debug("Session validation completed successfully in " + (stopTime - startTime) + " milliseconds."); } }
ValidatingSessionManager让其子类获得所有的Session(基础Session)
protected abstract Collection<Session> getActiveSessions();
DefaultSessionManager具体执行获得所有的基础Session
protected Collection<Session> getActiveSessions() { Collection<Session> active = sessionDAO.getActiveSessions(); return active != null ? active : Collections.<Session>emptySet(); }
各个基础Session自我校验
Collection<Session> activeSessions = getActiveSessions(); if (activeSessions != null && !activeSessions.isEmpty()) { for (Session s : activeSessions) { try { SessionKey key = new DefaultSessionKey(s.getId()); // 各个基础Session自我校验 validate(s, key); } catch (InvalidSessionException e) { if (log.isDebugEnabled()) { boolean expired = (e instanceof ExpiredSessionException); String msg = "Invalidated session with id [" + s.getId() + "]" + (expired ? " (expired)" : " (stopped)"); log.debug(msg); } invalidCount++; } } } protected void validate(Session session, SessionKey key) throws InvalidSessionException { try { doValidate(session); } catch (ExpiredSessionException ese) { onExpiration(session, ese, key); throw ese; } catch (InvalidSessionException ise) { onInvalidation(session, ise, key); throw ise; } } protected void doValidate(Session session) throws InvalidSessionException { if (session instanceof ValidatingSession) { // 基础Session根据自己拥有的属性值自我校验 ((ValidatingSession) session).validate(); } else { String msg = "The " + getClass().getName() + " implementation only supports validating " + "Session implementations of the " + ValidatingSession.class.getName() + " interface. " + "Please either implement this interface in your session implementation or override the " + AbstractValidatingSessionManager.class.getName() + ".doValidate(Session) method to perform validation."; throw new IllegalStateException(msg); } }
SimpleSession的自我校验
public void validate() throws InvalidSessionException { // 校验Session是否已经被停止使用了,如果Session已经被停止使用了则抛出异常 if (isStopped()) { String msg = "Session with id [" + getId() + "] has been " + "explicitly stopped. No further interaction under this session is " + "allowed."; throw new StoppedSessionException(msg); } // 校验Session是否到了过期时间,如果Session已经到期了则抛出异常 if (isTimedOut()) { expire(); Date lastAccessTime = getLastAccessTime(); long timeout = getTimeout(); Serializable sessionId = getId(); DateFormat df = DateFormat.getInstance(); String msg = "Session with id [" + sessionId + "] has expired. " + "Last access time: " + df.format(lastAccessTime) + ". Current time: " + df.format(new Date()) + ". Session timeout is set to " + timeout / MILLIS_PER_SECOND + " seconds (" + timeout / MILLIS_PER_MINUTE + " minutes)"; if (log.isTraceEnabled()) { log.trace(msg); } throw new ExpiredSessionException(msg); } } protected boolean isStopped() { return getStopTimestamp() != null; } protected boolean isTimedOut() { if (isExpired()) { return true; } long timeout = getTimeout(); if (timeout >= 0l) { Date lastAccessTime = getLastAccessTime(); if (lastAccessTime == null) { String msg = "session.lastAccessTime for session with id [" + getId() + "] is null. This value must be set at " + "least once, preferably at least upon instantiation. Please check the " + getClass().getName() + " implementation and ensure " + "this value will be set (perhaps in the constructor?)"; throw new IllegalStateException(msg); } long expireTimeMillis = System.currentTimeMillis() - timeout; Date expireTime = new Date(expireTimeMillis); return lastAccessTime.before(expireTime); } else { if (log.isTraceEnabled()) { log.trace("No timeout for session with id [" + getId() + "]. Session is not considered expired."); } } return false; } public boolean isExpired() { return expired; } protected void expire() { stop(); this.expired = true; } public void stop() { if (this.stopTimestamp == null) { this.stopTimestamp = new Date(); } }
SimpleSession过期后自己无能为力,只能抛出异常交给AbstractValidatingSessionManager抓住异常进行特殊处理
protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { log.trace("Session with id [{}] has expired.", s.getId()); try { // 执行过期时的行为 onExpiration(s); // 通知监听器,Session过期之后做点什么 notifyExpiration(s); } finally { // Session过期之后的行为,如在介质中删除Session afterExpired(s); } }
// 更新介质中的Session protected void onExpiration(Session session) { onChange(session); } protected void afterExpired(Session session) { }
ValidatingSessionManager关闭Session校验调度任务
protected synchronized void disableSessionValidation() { beforeSessionValidationDisabled(); SessionValidationScheduler scheduler = getSessionValidationScheduler(); if (scheduler != null) { try { // Session校验任务调度器禁用校验 scheduler.disableSessionValidation(); if (log.isInfoEnabled()) { log.info("Disabled session validation scheduler."); } } catch (Exception e) { if (log.isDebugEnabled()) { String msg = "Unable to disable SessionValidationScheduler. Ignoring (shutting down)..."; log.debug(msg, e); } } LifecycleUtils.destroy(scheduler); // 设置Session校验任务调度器为 null setSessionValidationScheduler(null); } }
Session任务调度器禁用Session校验
public void disableSessionValidation() { if (this.service != null) { this.service.shutdownNow(); } this.enabled = false; }