SessionManager
接口就两个方法,Session start(SessionContext context)
和Session getSession(SessionKey key) throws SessionException
其中start
方法,将会在创建session
时调用,即getSession(true)
时调用。
下面代码是DelegatingSubject.getSession方法
public Session getSession(boolean create) { if (log.isTraceEnabled()) { log.trace("attempting to get session; create = " + create + "; session is null = " + (this.session == null) + "; session has id = " + (this.session != null && session.getId() != null)); } if (this.session == null && create) { //added in 1.2: if (!isSessionCreationEnabled()) { String msg = "Session creation has been disabled for the current subject. This exception indicates " + "that there is either a programming error (using a session when it should never be " + "used) or that Shiro's configuration needs to be adjusted to allow Sessions to be created " + "for the current Subject. See the " + DisabledSessionException.class.getName() + " JavaDoc " + "for more."; throw new DisabledSessionException(msg); } log.trace("Starting session for host {}", getHost()); SessionContext sessionContext = createSessionContext(); Session session = this.securityManager.start(sessionContext); this.session = decorate(session); } return this.session; }
getSession(sessionKey)
根据sessionKey
获取session
,它会调用lookupSession(key)
,然后调用doGetSession(key)
。
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); }
doGetSession(sessionKey)
会调用retrieveSession
方法。
protected final Session doGetSession(final SessionKey key) throws InvalidSessionException { enableSessionValidationIfNecessary(); log.trace("Attempting to retrieve session with key {}", key); Session s = retrieveSession(key); if (s != null) { validate(s, key); } return s; }
retrieveSession(sessionKey)
会调用getSessionId(sessionKey)
,
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; } 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; }
getSessionId
是最重要的方法,我们要实现一个基于Token的SessionManager,我们可以继承DefaultWebSessionManager
,它默认是可以基于Cookie与Url来获得sessionId,所以我们只要实现getSessionId(ServletRequest request, ServletResponse reponse)
和onStart(Session session, SessionContext context)
方法即可。
@Override protected void onStart(Session session, SessionContext context) { super.onStart(session, context); if (WebUtils.isHttp(context)) { HttpServletRequest request = WebUtils.getHttpRequest(context); Serializable sessionId = session.getId(); request.setAttribute(AUTH_TOKEN, sessionId); } }
这里仅需在reqeust设置属性即可,记录的是sessionId。调用这个方法还会继续调用getSessionId,所以设置SessionId的操作放到那个方法去执行:
@Override protected Serializable getSessionId(ServletRequest request, ServletResponse response) { if (!(request instanceof HttpServletRequest)) { logger.debug("Current request is not an HttpServletRequest - cannot get session ID. Returning null."); return null; } HttpServletRequest httpRequest = WebUtils.toHttp(request); if (StringUtils.hasText(httpRequest.getHeader(AUTH_TOKEN))) { //从header中获取token String token = httpRequest.getHeader(AUTH_TOKEN); // 每次读取之后都把当前的token放入response中 HttpServletResponse httpResponse = WebUtils.toHttp(response); if (StringUtils.hasText(token)) { httpResponse.setHeader(AUTH_TOKEN, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header"); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, token); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); } //sessionIdUrlRewritingEnabled的配置为false,不会在url的后面带上sessionID request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, false); return token; } else { return super.getSessionId(request, response); } }
将SessionId放到response的header中,每次请求在request的header中设置token,将回去底层的session中查找。