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


    Web 项目使用shiro,针对这个问题可以重写DefaultWebSessionManager,将缓存数据存放到request中,这样可以保证每次请求(可能会多次调用doReadSession方法)只请求一次redis。
    具体扩展如下:

    添加MyWebSessionManager.java

    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.net  2016.03.31
     *
     */
    public class MyWebSessionManager extends DefaultSessionManager implements WebSessionManager {
        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 idString = currentId.toString();
            cookie.setValue(idString);
            cookie.saveTo(request, response);
            log.trace("Set session ID cookie for session with id {}", idString);
        }
    
        private void removeSessionIdCookie(HttpServletRequest request, HttpServletResponse response) {
            getSessionIdCookie().removeFrom(request, response);
        }
    
        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;
            return getSessionIdCookie().readValue(httpRequest, WebUtils.toHttp(response));
        }
    
        private Serializable getReferencedSessionId(ServletRequest request, ServletResponse response) {
    
            String id = getSessionIdCookieValue(request, response);
            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, 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 id;
        }
    
        // 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 ID from SessionKey [{}].  Returning null to indicate a "
                        + "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 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);
            }
    
    
    
            // ***************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 ID, usually as a Cookie, to associate with future
         * 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 ID cookie will be set.");
                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 ID cookie is disabled.  No cookie has been set for new session with id {}",
                        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 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 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 ID cookie.");
                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 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.");
                removeSessionIdCookie(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.");
            }
        }
    
        /**
         * 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;
        }
    }

    spring shiro xml 配置调整:

    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="sessionDAO" ref="redisSessionDAO" />
    </bean>

    变更为:

        <bean id="sessionManager" class="com.xiyinli.web.shiro.MyWebSessionManager">
            <property name="sessionDAO" ref="redisSessionDAO" />
        </bean>
  • 相关阅读:
    mybatis公用代码抽取到单独的mapper.xml文件
    mysql与oracle常用函数及数据类型对比00持续补充
    人民币在岸 离岸 中间价的含义与关系
    mysql hang and srv_error_monitor_thread using 100% cpu(已解决)
    long和BigDecimal引发的管理思考
    mybatis 3的TypeHandler深入解析(及null值的处理)
    mysql 5.7.17发布
    rabbitmq connection/channel/consumer/queue的数量关系详细分析
    rabbitMQ publish丢包分析
    INFO: task java:27465 blocked for more than 120 seconds不一定是cache太大的问题
  • 原文地址:https://www.cnblogs.com/007sx/p/7511310.html
Copyright © 2020-2023  润新知