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>

shiro+redis多次调用doReadSession方法的解决方案的更多相关文章

  1. Python/dotNET Redis服务连接客户端调用SET方法的同时获取Redis服务器返回的内容

    在用Python或dotNET redis客户端连接redis服务器的时候,当你调用客户端的SET方法后同时还想得到其返回的字符串,那么需要处理一下. 1. Redis Python redis客户端 ...

  2. springboot+shiro+redis项目整合

    介绍: Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最 ...

  3. SpringBoot+Shiro+Redis共享Session入门小栗子

    在单机版的Springboot+Shiro的基础上,这次实现共享Session. 这里没有自己写RedisManager.SessionDAO.用的 crazycake 写的开源插件 pom.xml ...

  4. spring boot shiro redis整合基于角色和权限的安全管理-Java编程

    一.概述 本博客主要讲解spring boot整合Apache的shiro框架,实现基于角色的安全访问控制或者基于权限的访问安全控制,其中还使用到分布式缓存redis进行用户认证信息的缓存,减少数据库 ...

  5. 基于redis的处理session的方法

    一个基于redis的处理session的方法,如下. <?php class Session_custom { private $redis; // redis实例 private $prefi ...

  6. shiro多Realm第一次调用不生效问题

    1. 由于最近自己写的一个项目上用到了多realm的使用,遇到了一个这样的问题: 1. 自己继承了BasicHttpAuthenticationFilter,实现了获取token,然后直接请求api的 ...

  7. Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参

    继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...

  8. springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3. springboot+shiro+redis(集群re ...

  9. springboot+shiro+redis(集群redis版)整合教程

    相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3.springboot+shiro+redis(单机red ...

随机推荐

  1. python 中文编码(一)

    我在学python的过程中,遇到的第二个问题,就是中文乱码,如今也算勉强入门了,在这里给大家说说我的经验,也算个新人引导吧.     在文章里,我会重点提到一个概念:有来有去. 即数据从哪里来,到哪里 ...

  2. zmap blacklist

    # From IANA IPv4 Special-Purpose Address Registry# http://www.iana.org/assignments/iana-ipv4-special ...

  3. 5. 集成学习(Ensemble Learning)GBDT

    1. 集成学习(Ensemble Learning)原理 2. 集成学习(Ensemble Learning)Bagging 3. 集成学习(Ensemble Learning)随机森林(Random ...

  4. 1. 集成学习(Ensemble Learning)原理

    1. 集成学习(Ensemble Learning)原理 2. 集成学习(Ensemble Learning)Bagging 3. 集成学习(Ensemble Learning)随机森林(Random ...

  5. UIWebView中JS与OC交互 WebViewJavascriptBridge的使用

    一.综述 现在很多的应用都会在多种平台上发布,所以很多程序猿们都开始使用Hybrid App的设计模式.就是在app上嵌入网页,只要写一份网页代码,就可以跑在不同的系统上.在iOS中,app多是通过W ...

  6. Android 异步任务——AsyncTask (附使用AsyncTask下载图片Demo)

    我们编程的时候经常需要处理同步任务和异步任务,在Android里面存在一个特性,就是UI线程是不安全的线程.所谓UI线程不安全也就是我们的主线程(进程启动的第一个线程)不能在线程外操作主线程的资源.因 ...

  7. 【Unity Shader】六、使用法线贴图(Normal Map)的Shader

    学习资料: http://www.sikiedu.com/course/37/task/456/show# http://www.sikiedu.com/course/37/task/458/show ...

  8. C#中Attribute和Property

    XAML是XML派生而来的语言,所以很多XML中的概念在XAML中是通用的. 为了表示同类标签中的某个标签与众不同,可以给它的特征(Attribute)赋值,为特征值赋值的语法如下: 非空标签:< ...

  9. genymotion 前端调试

    1. genymotion的安装见这里 注: a. 要先装VirtualBox, 再装genymotion b. VirtualBox不能太高,我装的是VirtualBox-4.1.40-101594 ...

  10. PCL的PNG文件和计算点云重心

    PCL提供节约一点云的值为一个PNG图像文件的可能方案.显然,这只能用有序的点云来完成,因为生成的图像的行和列将与点云的对应完全一致.例如,如果你从一个传感器Kinect或Xtion的点云,你可以用这 ...