7.SessionManager(session生命週期管理)
阿新 • • 發佈:2019-01-26
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);
}