1. 程式人生 > >7.SessionManager(session生命週期管理)

7.SessionManager(session生命週期管理)

SessionManager負責管理shiro自己封裝的session的生命週期。
為什麼shiro要自己封裝session?
1.可以為任意應用提供session支援,不依賴於底層容器
2.簡單擴容session管理容器,可以實現任何資料來源(redis,ehcache)來管理session,而不必擔心jvm記憶體溢位

這裡寫圖片描述
1.SessionManager

public interface SessionManager {

    //根據指定的上下文SessionContext交由SessionFactory初始化個新的session
    Session start(SessionContext context);

    //根據指定的上下文資訊(SessonId..)獲取一個session,如果不存在返回null,如果存在但是失效,則丟擲異常SessionException
Session getSession(SessionKey key) throws SessionException; }

2.AbstractSessionManager

/**AbstractSessionManager是SessionManager的頂層抽象類,主要提供Session存活時間管理的支援,
預設session存活時間是30分鐘

deprecate this class (see SHIRO-240):
這個類主要為子類提供一個常用的屬性“globalSessionTimeout”的支援,最初設計是為ServletContainerSessionManager
和AbstractNativeSessionManager提供session存活時間管理支援,但是ServletContainerSessionManager的實現並沒有
使用該“globalSessionTimeout”,這意味著只有AbstractNativeSessionManager需要使用。所以該類沒有存在必要了。
https://issues.apache.org/jira/browse/SHIRO-240
**/
public abstract class AbstractSessionManager implements SessionManager { protected static final long MILLIS_PER_SECOND = 1000; protected static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; protected static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; /** * Default main session timeout value, equal to {@code 30} minutes. */
public static final long DEFAULT_GLOBAL_SESSION_TIMEOUT = 30 * MILLIS_PER_MINUTE; //預設為30分鐘 private long globalSessionTimeout = DEFAULT_GLOBAL_SESSION_TIMEOUT; public AbstractSessionManager() { } public long getGlobalSessionTimeout() { return this.globalSessionTimeout; } //覆蓋預設session存活時間30分鐘 public void setGlobalSessionTimeout(long globalSessionTimeout) { this.globalSessionTimeout = globalSessionTimeout; } }

3.AbstractNativeSessionManager

/**
該類基礎父類AbstractSessionManager和實現NativeSessionManager介面(session操作方法規範),提供
session的start、stop、Expiration狀態的觸發通知。
**/
public abstract class AbstractNativeSessionManager extends AbstractSessionManager implements NativeSessionManager {

    private static final Logger log = LoggerFactory.getLogger(AbstractSessionManager.class);
    //SESSION狀態的觸發通知(start、stop、Expiration)
    private Collection<SessionListener> listeners;

    public AbstractNativeSessionManager() {
        this.listeners = new ArrayList<SessionListener>();
    }

    public void setSessionListeners(Collection<SessionListener> listeners) {
        this.listeners = listeners != null ? listeners : new ArrayList<SessionListener>();
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public Collection<SessionListener> getSessionListeners() {
        return this.listeners;
    }
    //實現SessionManager介面的start方法
    public Session start(SessionContext context) {
        //建立session
        Session session = createSession(context);
    //設定session過期時間
        applyGlobalSessionTimeout(session);
    //模板方法,子類對建立session後作出反應
        onStart(session, context);
    //建立session成功,迭代SessionListener執行onStart通知
        notifyStart(session);
        //返回一個包裝過客戶端層session,
        return createExposedSession(session, context);
    }

    //根據上下文SessionContext建立Session,返回的session需要持久化狀態,方便以後獲取使用
    protected abstract Session createSession(SessionContext context) throws AuthorizationException;
    //設定session過期時間
    protected void applyGlobalSessionTimeout(Session session) {
        session.setTimeout(getGlobalSessionTimeout());
    //session改變模板方法,子類對session改變作出反應。如:儲存改變後的session
        onChange(session);
    }

    //模板方法:子類對session start後作出反應
    protected void onStart(Session session, SessionContext context) {
    }
    //根據sessionKey獲取Session
    public Session getSession(SessionKey key) throws SessionException {
        Session session = lookupSession(key);
        return session != null ? createExposedSession(session, key) : null;
    }

    private Session lookupSession(SessionKey key) throws SessionException {
        if (key == null) {
            throw new NullPointerException("SessionKey argument cannot be null.");
        }
        return doGetSession(key);
    }
    //獲取session,如果session為null則拋異常UnknownSessionException
    private Session lookupRequiredSession(SessionKey key) throws SessionException {
        Session session = lookupSession(key);
        if (session == null) {
            String msg = "Unable to locate required Session instance based on SessionKey [" + key + "].";
            throw new UnknownSessionException(msg);
        }
        return session;
    }
    //子類實現抽象方法根據sessonKey獲取session
    protected abstract Session doGetSession(SessionKey key) throws InvalidSessionException;
    //封裝返回客戶端層session
    protected Session createExposedSession(Session session, SessionContext context) {
        return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
    }
    //封裝返回客戶端層session
    protected Session createExposedSession(Session session, SessionKey key) {
        return new DelegatingSession(this, new DefaultSessionKey(session.getId()));
    }

    //當session過期或者停止時,需要呼叫通知。再呼叫通知前先包裝下session為不可變session,防止SessionListener修改
    protected Session beforeInvalidNotification(Session session) {
        //ImmutableProxiedSession的實現即對session凡是有修改意向的方法進行重寫為拋異常。
        return new ImmutableProxiedSession(session);
    }

    //session start建立後呼叫SessionListener的onStart通知
    protected void notifyStart(Session session) {
        for (SessionListener listener : this.listeners) {
            listener.onStart(session);
        }
    }
    //session stop後呼叫SessionListener的onStop通知
    protected void notifyStop(Session session) {
        Session forNotification = beforeInvalidNotification(session);
        for (SessionListener listener : this.listeners) {
            listener.onStop(forNotification);
        }
    }
    //session Expiration後呼叫SessionListener的onExpiration通知
    protected void notifyExpiration(Session session) {
        Session forNotification = beforeInvalidNotification(session);
        for (SessionListener listener : this.listeners) {
            listener.onExpiration(forNotification);
        }
    }
    //根據sessionKey 獲取session的建立時間
    public Date getStartTimestamp(SessionKey key) {
        return lookupRequiredSession(key).getStartTimestamp();
    }
    //根據sessionKey 獲取session最後訪問時間
    public Date getLastAccessTime(SessionKey key) {
        return lookupRequiredSession(key).getLastAccessTime();
    }
    //根據sessionKey 獲取session過期時間.  負數:則永遠不過期 正數:毫秒內有效
    public long getTimeout(SessionKey key) throws InvalidSessionException {
        return lookupRequiredSession(key).getTimeout();
    }
    //根據sessionKey設定session過期時間
    public void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException {
        Session s = lookupRequiredSession(key);
        s.setTimeout(maxIdleTimeInMillis);
        onChange(s);
    }
    //根據sessionKey修改session最後訪問時間
    public void touch(SessionKey key) throws InvalidSessionException {
        Session s = lookupRequiredSession(key);
        s.touch();
        onChange(s);
    }
    //根據sessionKey獲取session 訪問IP..
    public String getHost(SessionKey key) {
        return lookupRequiredSession(key).getHost();
    }
    //根據sessionKey 獲取session裡的設定的屬性
    public Collection<Object> getAttributeKeys(SessionKey key) {
        Collection<Object> c = lookupRequiredSession(key).getAttributeKeys();
        if (!CollectionUtils.isEmpty(c)) {
        //如果不為空,則返回個不可修改的屬性集合
            return Collections.unmodifiableCollection(c);
        }
        return Collections.emptySet();
    }
    //根據sessionKey和屬性key獲取值
    public Object getAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
        return lookupRequiredSession(sessionKey).getAttribute(attributeKey);
    }
    //根據sessionKey、屬性key、屬性value設定屬性值
    public void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException {
        if (value == null) {
            removeAttribute(sessionKey, attributeKey);
        } else {
            Session s = lookupRequiredSession(sessionKey);
            s.setAttribute(attributeKey, value);
        //session改變模板方法,子類對session改變作出反應。如:儲存改變後的session
            onChange(s);
        }
    }
    //刪除session屬性值
    public Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException {
        Session s = lookupRequiredSession(sessionKey);
        Object removed = s.removeAttribute(attributeKey);
        if (removed != null) {
            //session改變模板方法,子類對session改變作出反應。如:儲存改變後的session
            onChange(s);
        }
        return removed;
    }
    //判斷session是否過期
    public boolean isValid(SessionKey key) {
        try {
        //檢查session是否過期
            checkValid(key);
            return true;
        } catch (InvalidSessionException e) {
            return false;
        }
    }
    //停止該session
    public void stop(SessionKey key) throws InvalidSessionException {
        Session session = lookupRequiredSession(key);
        try {
            if (log.isDebugEnabled()) {
                log.debug("Stopping session with id [" + session.getId() + "]");
            }
            session.stop();
        //執行session 停止模板方法
            onStop(session, key);
        //執行session停止通知
            notifyStop(session);
        } finally {
            afterStopped(session);
        }
    }
    //session停止模板方法
    protected void onStop(Session session, SessionKey key) {
        onStop(session);
    }
    //修改session停止後的狀態
    protected void onStop(Session session) {

        onChange(session);
    }
    //在session停止後執行該方法。如:刪除session
    protected void afterStopped(Session session) {
    }
    //檢查session是否有效,即獲取一個非null,非過期的session
    public void checkValid(SessionKey key) throws InvalidSessionException {
        //just try to acquire it.  If there is a problem, an exception will be thrown:
        lookupRequiredSession(key);
    }
    //對session修改作出反應的模板方法
    protected void onChange(Session s) {
    }
}

4.AbstractValidatingSessionManager

/**
AbstractValidatingSessionManager繼承於AbstractNativeSessionManager且實現了ValidatingSessionManager介面,該抽象類
主要提供session驗證和過期session檢測。

**/
public abstract class AbstractValidatingSessionManager extends AbstractNativeSessionManager
        implements ValidatingSessionManager, Destroyable {



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

    //預設每小時檢測一次
    public static final long DEFAULT_SESSION_VALIDATION_INTERVAL = MILLIS_PER_HOUR;
    //是否開啟session驗證檢測
    protected boolean sessionValidationSchedulerEnabled;

    //該介面主要代理SessionManager.validateSessions()方法,定時對session驗證檢測。如:過期的session則銷燬等
    protected SessionValidationScheduler sessionValidationScheduler;
    //該屬性是定義多長時間檢測一次,這個值需要根據使用者的實際情況來選擇,定義的太小,則檢測頻繁對系統性能有影響.
    protected long sessionValidationInterval;

    public AbstractValidatingSessionManager() {
        //預設開啟檢測
        this.sessionValidationSchedulerEnabled = true;
    //預設每小時檢測一次
        this.sessionValidationInterval = DEFAULT_SESSION_VALIDATION_INTERVAL;
    }

    public boolean isSessionValidationSchedulerEnabled() {
        return sessionValidationSchedulerEnabled;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void setSessionValidationSchedulerEnabled(boolean sessionValidationSchedulerEnabled) {
        this.sessionValidationSchedulerEnabled = sessionValidationSchedulerEnabled;
    }

    public void setSessionValidationScheduler(SessionValidationScheduler sessionValidationScheduler) {
        this.sessionValidationScheduler = sessionValidationScheduler;
    }

    public SessionValidationScheduler getSessionValidationScheduler() {
        return sessionValidationScheduler;
    }
    //確保sessionValidation功能是開啟的且有效的
    private void enableSessionValidationIfNecessary() {
        SessionValidationScheduler scheduler = getSessionValidationScheduler();
    //如果該類允許sessionValidation功能開啟且scheduler無效的,則執行enableSessionValidation建立SessionValidationScheduler
        if (isSessionValidationSchedulerEnabled() && (scheduler == null || !scheduler.isEnabled())) {
            enableSessionValidation();
        }
    }


    public void setSessionValidationInterval(long sessionValidationInterval) {
        this.sessionValidationInterval = sessionValidationInterval;
    }

    public long getSessionValidationInterval() {
        return sessionValidationInterval;
    }

    @Override
    protected final Session doGetSession(final SessionKey key) throws InvalidSessionException {
        //這裡我覺得會有併發問題,導致建立scheduler多次,且實現方式有問題,為什麼要在使用session的地方確保每次都開啟呢?
        enableSessionValidationIfNecessary();

        log.trace("Attempting to retrieve session with key {}", key);
    //根據sessionKey獲取session
        Session s = retrieveSession(key);
    //如果session不為null,則檢測session是否有效的
        if (s != null) {
            validate(s, key);
        }
        return s;
    }

    //根據sessionKey獲取session
    protected abstract Session retrieveSession(SessionKey key) throws UnknownSessionException;

    protected Session createSession(SessionContext context) throws AuthorizationException {
        enableSessionValidationIfNecessary();
        return doCreateSession(context);
    }
    //子類實現建立session
    protected abstract Session doCreateSession(SessionContext initData) throws AuthorizationException;
    //驗證session
    protected void validate(Session session, SessionKey key) throws InvalidSessionException {
        try {
            doValidate(session);
        } catch (ExpiredSessionException ese) {
        //session過期異常
            onExpiration(session, ese, key);
            throw ese;
        } catch (InvalidSessionException ise) {
        //session停止無效異常
            onInvalidation(session, ise, key);
            throw ise;
        }
    }
    //處理過期session
    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);
        }
    }

    protected void onExpiration(Session session) {
        onChange(session);
    }

    protected void afterExpired(Session session) {
    }
    //處理無效session
    protected void onInvalidation(Session s, InvalidSessionException ise, SessionKey key) {
        if (ise instanceof ExpiredSessionException) {
            onExpiration(s, (ExpiredSessionException) ise, key);
            return;
        }
        log.trace("Session with id [{}] is invalid.", s.getId());
        try {
        //對停止session作出處理:如:設定停止時間為最後訪問時間
            onStop(s);
        //呼叫session停止通知
            notifyStop(s);
        } finally {
        //session停止後作出處理,如:刪除session
            afterStopped(s);
        }
    }
    //執行session的驗證機制,實則交由session自己的validate();
    protected void doValidate(Session session) throws InvalidSessionException {
        if (session instanceof ValidatingSession) {
            ((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);
        }
    }

    /**
     * 這我也搞不懂幹嘛的。。
     * Subclass template hook in case per-session timeout is not based on
     * {@link org.apache.shiro.session.Session#getTimeout()}.
     * <p/>
     * <p>This implementation merely returns {@link org.apache.shiro.session.Session#getTimeout()}</p>
     *
     * @param session the session for which to determine session timeout.
     * @return the time in milliseconds the specified session may remain idle before expiring.
     */
    protected long getTimeout(Session session) {
        return session.getTimeout();
    }

    //確保sessionValidation有效
    protected void enableSessionValidation() {
        SessionValidationScheduler scheduler = getSessionValidationScheduler();
        if (scheduler == null) {
        //如果為null。則建立個新的SessionValidationScheduler且設定到this.SessionValidationScheduler
            scheduler = createSessionValidationScheduler();
            setSessionValidationScheduler(scheduler);
        }
        if (log.isInfoEnabled()) {
            log.info("Enabling session validation scheduler...");
        }
    //啟動SessionValidationScheduler裡的驗證機制
        scheduler.enableSessionValidation();
    //開啟session驗證後可以作出你的邏輯處理。目前shiro沒有實現任何功能,你可根據你需求來決定。
    //如:記錄每次開啟時間
        afterSessionValidationEnabled();
    }
    //建立SessionValidationScheduler
    protected SessionValidationScheduler createSessionValidationScheduler() {
        ExecutorServiceSessionValidationScheduler scheduler;

        if (log.isDebugEnabled()) {
            log.debug("No sessionValidationScheduler set.  Attempting to create default instance.");
        }
    //建立ExecutorServiceSessionValidationScheduler且設定本例項進去,因為scheduler需要代理本例項的validateSessions
        scheduler = new ExecutorServiceSessionValidationScheduler(this);
        scheduler.setInterval(getSessionValidationInterval());
        if (log.isTraceEnabled()) {
            log.trace("Created default SessionValidationScheduler instance of type [" + scheduler.getClass().getName() + "].");
        }
        return scheduler;
    }



    protected void afterSessionValidationEnabled() {
    }
    //銷燬SessionValidation
    protected void disableSessionValidation() {
        beforeSessionValidationDisabled();
        SessionValidationScheduler scheduler = getSessionValidationScheduler();
        if (scheduler != null) {
            try {
            //先執行SessionValidationScheduler自己的disableSessionValidation
                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);
        //設定this.SessionValidationScheduler為null
            setSessionValidationScheduler(null);
        }
    }

    protected void beforeSessionValidationDisabled() {
    }

    public void destroy() {
        disableSessionValidation();
    }

    //所有session真正的檢測驗證邏輯
    public void validateSessions() {
        if (log.isInfoEnabled()) {
            log.info("Validating all active sessions...");
        }
        //用來記錄當前這一批次檢驗到session過期或者無效的總數
        int invalidCount = 0;
        //獲取sessionDAO中當前所有存在session,包括無效、過期的
        Collection<Session> activeSessions = getActiveSessions();
        /**
        當你使用者量如果非常大時候,假如有1億個session,那麼迭代會花很長時間且在這一階段裡會影響效能.可以分批次
    **/
        if (activeSessions != null && !activeSessions.isEmpty()) {
            for (Session s : activeSessions) {
                try {
                    //simulate a lookup key to satisfy the method signature.
                    //this could probably stand to be cleaned up in future versions:
                    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++;
                }
            }
        }

        if (log.isInfoEnabled()) {
            String msg = "Finished session validation.";
            if (invalidCount > 0) {
                msg += "  [" + invalidCount + "] sessions were stopped.";
            } else {
                msg += "  No sessions were stopped.";
            }
            log.info(msg);
        }
    }
    //獲取sessionDAO中當前所有存在session,包括無效、過期的
    protected abstract Collection<Session> getActiveSessions();
}

5.DefaultSessionManager

/**
DefaultSessionManager繼承AbstractValidatingSessionManager且實現CacheManagerAware介面,
該具體實現類實現快取介面CacheManagerAware只是為了讓SessionDAO使用。
所有關於session的狀態的改變都是交由DefaultSessionManager的sessionDAO處理。
**/
public class DefaultSessionManager extends AbstractValidatingSessionManager implements CacheManagerAware {

    private static final Logger log = LoggerFactory.getLogger(DefaultSessionManager.class);
    //SessionFactory用來根據上下文建立新session
    private SessionFactory sessionFactory;
    //SessionDAO實現了快取機制,所以使用者只要實現第三方快取,不依賴於JVM,就不必擔心session過多導致記憶體溢位
    protected SessionDAO sessionDAO;  //todo - move SessionDAO up to AbstractValidatingSessionManager?
    //快取管理器
    private CacheManager cacheManager;
    /**
    是否刪除session,如果session過期或者無效後。但是有些系統需要獲取及時過期或者無效的SESSION。
    預設為true。如果設定為false,需要使用者自己有辦法在外部去管理過期或者無效session
    **/
    private boolean deleteInvalidSessions;

    public DefaultSessionManager() {
        this.deleteInvalidSessions = true;
        this.sessionFactory = new SimpleSessionFactory();
        this.sessionDAO = new MemorySessionDAO();
    }

    public void setSessionDAO(SessionDAO sessionDAO) {
        this.sessionDAO = sessionDAO;
    //設定CacheManager到SessionDAO
        applyCacheManagerToSessionDAO();
    }

    public SessionDAO getSessionDAO() {
        return this.sessionDAO;
    }

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }


    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    //session過期或者無效後是否刪除
    public boolean isDeleteInvalidSessions() {
        return deleteInvalidSessions;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void setDeleteInvalidSessions(boolean deleteInvalidSessions) {
        this.deleteInvalidSessions = deleteInvalidSessions;
    }
    //設定快取管理器
    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    //設定快取管理器到SessionDAO
        applyCacheManagerToSessionDAO();
    }


    private void applyCacheManagerToSessionDAO() {
        //如果快取管理器和SessionDAO都不null,且SessionDAO實現了CacheManagerAware,則設定CacheManager到SessionDAO
        if (this.cacheManager != null && this.sessionDAO != null && this.sessionDAO instanceof CacheManagerAware) {
            ((CacheManagerAware) this.sessionDAO).setCacheManager(this.cacheManager);
        }
    }

    //根據session上下文SessionContext建立Session
    protected Session doCreateSession(SessionContext context) {
        //呼叫SessionFactory建立session
        Session s = newSessionInstance(context);
        if (log.isTraceEnabled()) {
            log.trace("Creating session for host {}", s.getHost());
        }
    //儲存session到SessionDAO
        create(s);
        return s;
    }
    //呼叫SessionFactory建立session
    protected Session newSessionInstance(SessionContext context) {
        return getSessionFactory().createSession(context);
    }

    //儲存session到SessionDAO
    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);
    }

    @Override
    protected void afterExpired(Session session) {
        if (isDeleteInvalidSessions()) {
            delete(session);
        }
    }

    protected void onChange(Session session) {
        sessionDAO.update(session);
    }

    protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        Serializable sessionId = getSessionId(sessionKey);
        if (sessionId == null) {
            log.debug("Unable to resolve session ID from SessionKey [{}].  Returning null to indicate a " +
                    "session could not be found.", sessionKey);
            return null;
        }
    //根據sessionID從SessionDAO中獲取相對應的Session
        Session s = retrieveSessionFromDataSource(sessionId);
        if (s == null) {
            //session ID was provided, meaning one is expected to be found, but we couldn't find one:
            String msg = "Could not find session with ID [" + sessionId + "]";
            throw new UnknownSessionException(msg);
        }
        return s;
    }
    //獲取sessionKey裡的SessionID
    protected Serializable getSessionId(SessionKey sessionKey) {
        return sessionKey.getSessionId();
    }
    //根據sessionID從SessionDAO中獲取相對應的Session
    protected Session retrieveSessionFromDataSource(Serializable sessionId) throws UnknownSessionException {
        return sessionDAO.readSession(sessionId);
    }
    //刪除session
    protected void delete(Session session) {
        sessionDAO.delete(session);
    }
    //獲取SessionDAO的存在session
    protected Collection<Session> getActiveSessions() {
        Collection<Session> active = sessionDAO.getActiveSessions();
        return active != null ? active : Collections.<Session>emptySet();
    }

}

6.DefaultWebSessionManager

//基於WEB的SessionManager的實現  
public class DefaultWebSessionManager extends DefaultSessionManager implements WebSessionManager {

    private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class);
    //Cookie基本模板
    private Cookie sessionIdCookie;
    //是否儲存sessionID到cookie中,預設沒有設定存活時間,即儲存在瀏覽器開闢的記憶體中,沒有持久化在硬碟上
    private boolean sessionIdCookieEnabled;

    public DefaultWebSessionManager() {
        Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
    //HttpOnly是防止使用者通過js指令碼獲取cookie
        cookie.setHttpOnly(true); //more secure, protects against XSS attacks
        this.sessionIdCookie = cookie;
    //預設允許儲存sessionID到cookie中
        this.sessionIdCookieEnabled = true;
    }
    //獲取模板cookie
    public Cookie getSessionIdCookie() {
        return sessionIdCookie;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void setSessionIdCookie(Cookie sessionIdCookie) {
        this.sessionIdCookie = sessionIdCookie;
    }

    public boolean isSessionIdCookieEnabled() {
        return sessionIdCookieEnabled;
    }

    @SuppressWarnings({"UnusedDeclaration"})
    public void setSessionIdCookieEnabled(boolean sessionIdCookieEnabled) {
        this.sessionIdCookieEnabled = sessionIdCookieEnabled;
    }
    //儲存sessionID到cookie中
    private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
        if (currentId == null) {
            String msg = "sessionId cannot be null when persisting for subsequent requests.";
            throw new IllegalArgumentException(msg);
        }
        Cookie template = getSessionIdCookie();
        Cookie cookie = new SimpleCookie(template);
        String idString = currentId.toString();
        cookie.setValue(idString);
        cookie.saveTo(request, response);
        log.trace("Set session ID cookie for session with id {}", idString);
    }
    //刪除瀏覽器SessionID cookie
    private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
        getSessionIdCookie().removeFrom(request, response);
    }
    //根據sessionID Cookie獲取sessionid
    private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
        if (!isSessionIdCookieEnabled()) {
            log.debug("Session ID cookie is disabled - session id will not be acquired from a request cookie.");
            return null;
        }
        if (!(request instanceof HttpServletRequest)) {
            log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie.  Returning null.");
            return null;
        }
        HttpServletRequest httpRequest = (HttpServletRequest) request;
    //從Cookie中讀取sessionID
        return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
    }

    //該方法嘗試通過多種辦法獲取SessionID
    private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
        //首先在cookie中獲取sessionID
        String id = getSessionIdCookieValue(request, response);
        if (id != null) {
        //設定獲取SessionID的來源 Cookie
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                    ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
        } else {
            //cookie不存在,嘗試通過uri中獲取
            id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);

            if (id == null) {
                //嘗試中request的引數中獲取
                String name = getSessionIdName();

                id = request.getParameter(name);
                if (id == null) {

                    id = request.getParameter(name.toLowerCase());
                }
            }
            if (id != null) {
        //設定獲取SessionID的來源 URL
                request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
                        ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
            }
        }
        if (id != null) {
        //設定SessionUD 
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            //設定SessionID有效.
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
        }
        return id;
    }

    //通過URI獲取SessionID
    private String getUriPathSegmentParamValue(ServletRequest servletRequest, String paramName) {

        if (!(servletRequest instanceof HttpServletRequest)) {
            return null;
        }
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        String uri = request.getRequestURI();
        if (uri == null) {
            return null;
        }

        int queryStartIndex = uri.indexOf('?');
        if (queryStartIndex >= 0) { //get rid of the query string
            uri = uri.substring(0, queryStartIndex);
        }

        int index = uri.indexOf(';'); //now check for path segment parameters:
        if (index < 0) {
            //no path segment params - return:
            return null;
        }

        //there are path segment params, let's get the last one that may exist:

        final String TOKEN = paramName + "=";

        uri = uri.substring(index+1); //uri now contains only the path segment params

        //we only care about the last JSESSIONID param:
        index = uri.lastIndexOf(TOKEN);
        if (index < 0) {
            //no segment param:
            return null;
        }

        uri = uri.substring(index + TOKEN.length());

        index = uri.indexOf(';'); //strip off any remaining segment params:
        if(index >= 0) {
            uri = uri.substring(0, index);
        }

        return uri; //what remains is the value
    }

    //獲取sessionid儲存的名字
    private String getSessionIdName() {
        String name = this.sessionIdCookie != null ? this.sessionIdCookie.getName() : null;
        if (name == null) {
            name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
        }
        return name;
    }
    //封裝一個基於WEB(包含request和response的支援)的 客戶端層session,該方法主要用在新建立session
    protected Session createExposedSession(Session session, SessionContext context) {
        if (!WebUtils.isWeb(context)) {
            return super.createExposedSession(session, context);
        }
        ServletRequest request = WebUtils.getRequest(context);
        ServletResponse response = WebUtils.getResponse(context);
        SessionKey key = new WebSessionKey(session.getId(), request, response);
        return new DelegatingSession(this, key);
    }