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>
shiro+redis多次调用doReadSession方法的解决方案的更多相关文章
- Python/dotNET Redis服务连接客户端调用SET方法的同时获取Redis服务器返回的内容
在用Python或dotNET redis客户端连接redis服务器的时候,当你调用客户端的SET方法后同时还想得到其返回的字符串,那么需要处理一下. 1. Redis Python redis客户端 ...
- springboot+shiro+redis项目整合
介绍: Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最 ...
- SpringBoot+Shiro+Redis共享Session入门小栗子
在单机版的Springboot+Shiro的基础上,这次实现共享Session. 这里没有自己写RedisManager.SessionDAO.用的 crazycake 写的开源插件 pom.xml ...
- spring boot shiro redis整合基于角色和权限的安全管理-Java编程
一.概述 本博客主要讲解spring boot整合Apache的shiro框架,实现基于角色的安全访问控制或者基于权限的访问安全控制,其中还使用到分布式缓存redis进行用户认证信息的缓存,减少数据库 ...
- 基于redis的处理session的方法
一个基于redis的处理session的方法,如下. <?php class Session_custom { private $redis; // redis实例 private $prefi ...
- shiro多Realm第一次调用不生效问题
1. 由于最近自己写的一个项目上用到了多realm的使用,遇到了一个这样的问题: 1. 自己继承了BasicHttpAuthenticationFilter,实现了获取token,然后直接请求api的 ...
- Asp.Net Core SignalR 用泛型Hub优雅的调用前端方法及传参
继续学习 最近一直在使用Asp.Net Core SignalR(下面成SignalR Core)为小程序提供websocket支持,前端时间也发了一个学习笔记,在使用过程中稍微看了下它的源码,不得不 ...
- springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)
相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3. springboot+shiro+redis(集群re ...
- springboot+shiro+redis(集群redis版)整合教程
相关教程: 1. springboot+shiro整合教程 2. springboot+shiro+redis(单机redis版)整合教程 3.springboot+shiro+redis(单机red ...
随机推荐
- Cisco交换机堆叠与HSRP之间的区别
随着Internet的日益普及,人们对网络的依赖性也越来越强.这同时对网络的稳定性提出了更高的要求,人们自然想到了基于设备的备份结构,就像在服务器中为提高数据的安全性而采用双硬盘结构一样.核心交换机是 ...
- java多线程15 :wait()和notify() 的生产者/消费者模式
什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...
- 【转】 oracle 层次查询判断叶子和根节点
Oracle 9i判断是叶子或根节点,是比较麻烦的一件事情,SQL演示脚本如下: DROP TABLE idb_hierarchical; create TABLE idb_hierarchical ...
- docker探索-CentOS7中配置Docker的yum源并升级安装docker1.13(十)
此处使用的是CentOS7,内核版本为 [root@localhost ~]# uname -r -.el7.x86_64 该版本下,配置了yum的源为阿里的镜像源,具体的配置方法可以参见阿里镜像源配 ...
- Java API学习(一) ArrayList源码学习
ArrayList在平常用的还挺多的,用起来十分舒服,顺手.这里来学习一下它的源码. 类定义 下面是类的定义: public class ArrayList<E> extends Abst ...
- where子查询
限定查询(WHERE子句) 之前的查询是将一张表的全部记录查询出来,那么现在如果希望可以根据指定的条件查询的话,则必须指定限定查询. 格式: 的雇员的信息 l 使用“<>”完成 l S ...
- CPP_类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数
类默认函数:构造函数,拷贝构造函数,赋值函数和析构函数 // person.h #ifndef _PERSON_H_ #define _PERSON_H_ class Person{ public : ...
- Charles proxy tools 移动开发调试
简介 本文为InfoQ中文站特供稿件,首发地址为:文章链接.如需转载,请与InfoQ中文站联系. Charles是在Mac下常用的截取网络封包的工具,在做iOS开发时,我们为了调试与服务器端的网络通讯 ...
- git基本操作:上传代码
利用git将本地代码提交到远程服务器一般分为如下5个步骤: 一.查看当前文件的状态 使用git status命令可以查看有那些文件进行了修改,一般有下面几个状态: 1.Untracked: 未跟踪, ...
- WebRTC网关服务器单端口方案实现
标准WebRTC连接建立流程 这里描述的是Trickle ICE过程,并且省略了通话发起与接受的信令部分.流程如下: 1) WebRTC A通过Signal Server转发SDP OFFER到Web ...