西湖鱼 发表于 2017-12-22 07:54:37

shiro+redis多次调用doReadSession方法的解决方案

package com.xiyinli.web.shiro;  

  

import org.apache.shiro.session.ExpiredSessionException;  

import org.apache.shiro.session.InvalidSessionException;  

import org.apache.shiro.session.Session;  

import org.apache.shiro.session.UnknownSessionException;  

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.Cookie;  

import org.apache.shiro.web.servlet.ShiroHttpServletRequest;  

import org.apache.shiro.web.servlet.ShiroHttpSession;  

import org.apache.shiro.web.servlet.SimpleCookie;  

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;  

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;  

  

/**  *
  * 自定义WebSessionManager,用于替代DefaultWebSessionManager;
  * 解决:
  *      在shiro的一次认证过程中会调用10次左右的 doReadSession,如果使用内存缓存这个问题不大。
  *      但是如果使用redis,而且子网络情况不是特别好的情况下这就成为问题了。我简单在我的环境下测试了一下。
  *      一次redis请求需要80 ~ 100 ms, 一下来10次,我们一次认证就需要10 * 100 = 1000 ms, 这个就是我们无法接受的了。
  *
  * 大部分代码都是从DefaultWebSessionManager中复制过来的,扩展点在 line:200~212、225~229
  *
  *
@author Goma oma1989@yeah.net2016.03.31  *
*/
  
public>  private static final Logger log = LoggerFactory.getLogger(DefaultWebSessionManager.class);
  

  private Cookie sessionIdCookie;
  private boolean sessionIdCookieEnabled;
  

  public MyWebSessionManager() {
  Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
  cookie.setHttpOnly(true); // more secure, protects against XSS attacks
  this.sessionIdCookie = cookie;
  this.sessionIdCookieEnabled = true;
  }
  

  public Cookie getSessionIdCookie() {
  return sessionIdCookie;
  }
  

  public void setSessionIdCookie(Cookie sessionIdCookie) {
  this.sessionIdCookie = sessionIdCookie;
  }
  

  public boolean isSessionIdCookieEnabled() {
  return sessionIdCookieEnabled;
  }
  

  public void setSessionIdCookieEnabled(boolean sessionIdCookieEnabled) {
  this.sessionIdCookieEnabled = sessionIdCookieEnabled;
  }
  

  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>  cookie.setValue(idString);
  cookie.saveTo(request, response);

  log.trace("Set session>  }
  

  private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
  getSessionIdCookie().removeFrom(request, response);
  }
  

  private String getSessionIdCookieValue(ServletRequest request, ServletResponse response) {
  if (!isSessionIdCookieEnabled()) {

  log.debug("Session>  return null;
  }
  if (!(request instanceof HttpServletRequest)) {

  log.debug("Current request is not an HttpServletRequest - cannot get session>  return null;
  }
  HttpServletRequest httpRequest = (HttpServletRequest) request;
  return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
  }
  

  private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
  


  String>  if (id != null) {
  request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
  ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
  } else {
  // not in a cookie, or cookie is disabled - try the request URI as a
  // fallback (i.e. due to URL rewriting):
  

  // try the URI path segment parameters first:
  id = getUriPathSegmentParamValue(request, ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
  

  if (id == null) {
  // not a URI path segment parameter, try the query parameters:
  String name = getSessionIdName();
  id = request.getParameter(name);
  if (id == null) {
  // try lowercase:
  id = request.getParameter(name.toLowerCase());
  }
  }
  if (id != null) {
  request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE,
  ShiroHttpServletRequest.URL_SESSION_ID_SOURCE);
  }
  }
  if (id != null) {

  request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID,>  // automatically mark it valid here. If it is invalid, the
  // onUnknownSession method below will be invoked and we'll remove
  // the attribute at that time.
  
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
  }

  return>  }
  

  // SHIRO-351
  // also see
  // http://cdivilly.wordpress.com/2011/04/22/java-servlets-uri-parameters/
  // since 1.2.2
  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
  
    }
  

  //------------------------------------------------------------------------------------------------------------------
  /**
  * Modify By Goma
  */
  protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
  Serializable sessionId = getSessionId(sessionKey);
  if (sessionId == null) {

  log.debug("Unable to resolve session>  + "session could not be found.", sessionKey);
  return null;
  }
  

  

  // ***************Add By Goma****************
  ServletRequest request = null;
  if (sessionKey instanceof WebSessionKey) {
  request = ((WebSessionKey) sessionKey).getServletRequest();
  }
  

  if (request != null) {
  Object s = request.getAttribute(sessionId.toString());
  if (s != null) {
  return (Session) s;
  }
  }
  // ***************Add By Goma****************
  

  

  Session s = retrieveSessionFromDataSource(sessionId);
  if (s == null) {

  // session>  // we couldn't find one:

  String msg = "Could not find session with>  throw new UnknownSessionException(msg);
  }
  

  

  

  // ***************Add By Goma****************
  if (request != null) {
  request.setAttribute(sessionId.toString(),s);
  }
  // ***************Add By Goma****************
  

  

  return s;
  }
  //------------------------------------------------------------------------------------------------------------------
  

  // since 1.2.1
  private String getSessionIdName() {
  String name = this.sessionIdCookie != null ? this.sessionIdCookie.getName() : null;
  if (name == null) {
  name = ShiroHttpSession.DEFAULT_SESSION_ID_NAME;
  }
  return name;
  }
  

  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);
  }
  

  protected Session createExposedSession(Session session, SessionKey key) {
  if (!WebUtils.isWeb(key)) {
  return super.createExposedSession(session, key);
  }
  

  ServletRequest request = WebUtils.getRequest(key);
  ServletResponse response = WebUtils.getResponse(key);
  SessionKey sessionKey = new WebSessionKey(session.getId(), request, response);
  return new DelegatingSession(this, sessionKey);
  }
  

  /**

  * Stores the Session's>  * requests.
  *
  * @param session
  *            the session that was just {@link #createSession created}.
  */
  @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>  return;
  

  }
  HttpServletRequest request = WebUtils.getHttpRequest(context);
  HttpServletResponse response = WebUtils.getHttpResponse(context);
  

  if (isSessionIdCookieEnabled()) {
  Serializable sessionId = session.getId();
  storeSessionId(sessionId, request, response);
  } else {

  log.debug("Session>  session.getId());
  }
  

  request.removeAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE);
  request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW, Boolean.TRUE);
  }
  

  @Override
  public Serializable getSessionId(SessionKey key) {

  Serializable>  if (id == null && WebUtils.isWeb(key)) {
  ServletRequest request = WebUtils.getRequest(key);
  ServletResponse response = WebUtils.getResponse(key);
  id = getSessionId(request, response);
  }

  return>  }
  

  protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
  return getReferencedSessionId(request, response);
  }
  

  @Override
  protected void onExpiration(Session s, ExpiredSessionException ese, SessionKey key) {
  super.onExpiration(s, ese, key);
  onInvalidation(key);
  }
  

  @Override
  protected void onInvalidation(Session session, InvalidSessionException ise, SessionKey key) {
  super.onInvalidation(session, ise, key);
  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>  removeSessionIdCookie(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>  }
  }
  

  @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>  removeSessionIdCookie(request, response);
  } else {
  log.debug("SessionKey argument is not HTTP compatible or does not have an HTTP request/response "

  + "pair. Session>  }
  }
  

  /**
  * This is a native session manager implementation, so this method returns
  * {@code false} always.
  *
  * @return {@code false} always
  * @since 1.2
  */
  public boolean isServletContainerSessions() {
  return false;
  }
  
}
页: [1]
查看完整版本: shiro+redis多次调用doReadSession方法的解决方案