原文地址:https://www.cnblogs.com/elvinle/p/9272076.html
前言:
在前后端項目中, 前端有可能會要求, 后台返回一個 sessionId 給他, 然后他在請求后台接口時, 把這個sessionId 帶給后台, 后台拿到這個sessionId , 就能識別, 是那個用戶.
在shiro中, 會返回一個 JSESSIONID , 其實就是sessionId .
如果不想把sessionId 放在參數中, 或者貼在鏈接后面, 暴露給用戶, 那么我們還能選擇把 sessionId 放在請求頭中, 這樣比較隱秘一些. 不會那么明顯.
一. DefaultWebSessionManager
在配置shiro的時候, 我們經常會配置 org.apache.shiro.web.session.mgt.DefaultWebSessionManager , 那么我們先來看看這個類中的相關方法吧.
1. onStart 方法
在這個方法中, 我們其實可以很明顯的看到有一個storeSessionId 方法, 在這個方法中, 會將 sessionId 存入到cookie中.
如果我們可以繼承這個類, 然后重寫onStart方法, 那么是否可以實現, 將sessionId放到響應頭中去, 前端可以從響應頭中獲取到sessionId.
那既然有處理sesisonId的方法, 那么是否有get sessionId的方法呢? 如果有這個方法, 我們重寫它, 改為從請求頭中讀取sessionid, 不就可以了么.
那來找一下看看:
確實有的. 里面的具體實現, 就不看了, 不是這篇的主題.
二. 自定義 DefaultWebSessionManager 類
import org.apache.commons.lang.StringUtils; import org.apache.shiro.session.ExpiredSessionException; import org.apache.shiro.session.InvalidSessionException; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.session.mgt.DelegatingSession; import org.apache.shiro.session.mgt.SessionContext; import org.apache.shiro.session.mgt.SessionKey; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.WebSessionKey; import org.apache.shiro.web.session.mgt.WebSessionManager; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.Serializable; public class DefaultHeaderSessionManager extends DefaultSessionManager implements WebSessionManager { private static final Logger log = LoggerFactory.getLogger(DefaultHeaderSessionManager.class); private final String X_AUTH_TOKEN = "x-auth-token"; @Override protected void onStart(Session session, SessionContext context) { //super.onStart(session, context); if (!WebUtils.isHttp(context)) { log.debug("SessionContext argument is not HTTP compatible or does not have an HTTP request/response pair. No session ID cookie will be set."); } else { HttpServletRequest request = WebUtils.getHttpRequest(context); HttpServletResponse response = WebUtils.getHttpResponse(context); Serializable sessionId = session.getId(); this.storeSessionId(sessionId, request, response); request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE); } } //獲取sessionid @Override public Serializable getSessionId(SessionKey key) { Serializable id = super.getSessionId(key); if (id == null && WebUtils.isWeb(key)) { ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); id = getSessionId(request, response); } return id; } protected Serializable getSessionId(ServletRequest request, ServletResponse response) { return this.getReferencedSessionId(request, response); } // 請求頭中獲取 sessionId 並把sessionId 放入 response 中 private String getSessionIdHeaderValue(ServletRequest request, ServletResponse response) { if (!(request instanceof HttpServletRequest)) { log.debug("Current request is not an HttpServletRequest - cannot get session ID cookie. Returning null."); return null; } else { HttpServletRequest httpRequest = (HttpServletRequest) request; // 在request 中 讀取 x-auth-token 信息 作為 sessionId String sessionId = httpRequest.getHeader(this.X_AUTH_TOKEN); // 每次讀取之后 都把當前的 sessionId 放入 response 中 HttpServletResponse httpResponse = (HttpServletResponse) response; if (StringUtils.isNotEmpty(sessionId)) { httpResponse.setHeader(this.X_AUTH_TOKEN, sessionId); log.info("Current session ID is {}", sessionId); } return sessionId; } } //獲取sessionid private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) { String id = this.getSessionIdHeaderValue(request, response); //DefaultWebSessionManager 中代碼 直接copy過來 if (id != null) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header"); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); } //不會把sessionid放在URL后 request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.FALSE); return id; } // 移除sessionid 並設置為 deleteMe 標識 private void removeSessionIdHeader(HttpServletRequest request, HttpServletResponse response) { response.setHeader(this.X_AUTH_TOKEN, "deleteMe"); } /** * 把sessionId 放入 response header 中 * onStart時調用 * 沒有sessionid時 會產生sessionid 並放入 response header中 */ private void storeSessionId(Serializable currentId, HttpServletRequest ignored, HttpServletResponse response) { if (currentId == null) { String msg = "sessionId cannot be null when persisting for subsequent requests."; throw new IllegalArgumentException(msg); } else { String idString = currentId.toString(); response.setHeader(this.X_AUTH_TOKEN, idString); log.info("Set session ID header for session with id {}", idString); log.trace("Set session ID header for session with id {}", idString); } } // 創建session @Override protected Session createExposedSession(Session session, SessionContext context) { if (!WebUtils.isWeb(context)) { return super.createExposedSession(session, context); } else { ServletRequest request = WebUtils.getRequest(context); ServletResponse response = WebUtils.getResponse(context); SessionKey key = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, key); } } @Override protected Session createExposedSession(Session session, SessionKey key) { if (!WebUtils.isWeb(key)) { return super.createExposedSession(session, key); } else { ServletRequest request = WebUtils.getRequest(key); ServletResponse response = WebUtils.getResponse(key); SessionKey sessionKey = new WebSessionKey(session.getId(), request, response); return new DelegatingSession(this, sessionKey); } } @Override protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) { super.onExpiration(s, ese, key); this.onInvalidation(key); } @Override protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) { super.onInvalidation(session, ise, key); this.onInvalidation(key); } private void onInvalidation(SessionKey key) { ServletRequest request = WebUtils.getRequest(key); if (request != null) { request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID); } if (WebUtils.isHttp(key)) { log.debug("Referenced session was invalid. Removing session ID header."); this.removeSessionIdHeader(WebUtils.getHttpRequest(key), WebUtils.getHttpResponse(key)); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response pair. Session ID cookie will not be removed due to invalidated session."); } } @Override protected void onStop(Session session, SessionKey key) { super.onStop(session, key); if (WebUtils.isHttp(key)) { HttpServletRequest request = WebUtils.getHttpRequest(key); HttpServletResponse response = WebUtils.getHttpResponse(key); log.debug("Session has been stopped (subject logout or explicit stop). Removing session ID cookie."); this.removeSessionIdHeader(request, response); } else { log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response pair. Session ID cookie will not be removed due to stopped session."); } } @Override public boolean isServletContainerSessions() { return false; } }
三. 配置
<bean id="sessionManager" class="ccdc.zykt.web.shiro.headtoken.DefaultHeaderSessionManager"> <property name="sessionDAO" ref="sessionDAO"/> <property name="globalSessionTimeout" value="3600000"/> <property name="sessionValidationInterval" value="3600000"/> </bean> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"/>
四. 演示方法
1. 通過瀏覽器訪問登錄接口, 拿到sessionId, 也就是這里的 x-auth-token
2. 打開postman, 將x-auth-token放入到請求頭中, 在訪問接口
確實可以識別用戶, 拿到數據.
自定義defautlwebsessionmanager
import com.ydzbinfo.hussar.core.util.ToolUtil; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import java.io.Serializable; public class AdminWebSessionManager extends DefaultWebSessionManager { public static final String LOGIN_TOKEN_KEY = "Authorization"; private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request"; public AdminWebSessionManager() { } protected Serializable getSessionId(ServletRequest request, ServletResponse response) { HttpServletRequest req = (HttpServletRequest)request; String id = req.getHeader(LOGIN_TOKEN_KEY); if (!ToolUtil.isEmpty(id)) { request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "Stateless request"); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id); request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE); return id; } else { return super.getSessionId(request, response); } } }
shiro配置文件:
@Bean public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager, Collection<SessionListener> listeners, SessionDAO sessionDAO) { DefaultWebSessionManager sessionManager = new AdminWebSessionManager(); sessionManager.setSessionValidationScheduler(this.sessionValidationScheduler(sessionManager)); sessionManager.setSessionValidationInterval((long) (this.hussarProperties.getSessionValidationInterval() * this.kilo)); sessionManager.setGlobalSessionTimeout((long) (this.hussarProperties.getSessionInvalidateTime() * this.kilo)); sessionManager.setDeleteInvalidSessions(true); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setSessionIdUrlRewritingEnabled(false); sessionManager.setSessionListeners(listeners); sessionManager.setCacheManager(cacheShiroManager); sessionManager.setSessionDAO(sessionDAO); sessionManager.setSessionIdCookieEnabled(true); Cookie cookie = new SimpleCookie(this.globalProperties.getTitle() + "_hussarShiroCookie"); cookie.setHttpOnly(true); sessionManager.setSessionIdCookie(cookie); return sessionManager; }
參考文章: