前言:
在前后端项目中, 前端有可能会要求, 后台返回一个 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放入到请求头中, 在访问接口
确实可以识别用户, 拿到数据.