用 Spring+Shiro+Redis 实现服务器集群会话管理

yk624459085 2014-02-19 04:15:40
怎样用 Spring+Shiro+Redis 实现服务器集群会话管理呢? 最好能贴上代码。
...全文
28949 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
小丑哥_V5 2015-04-14
  • 打赏
  • 举报
回复
兄弟你可以到我博客看看shiro的文章吧,都有你想要的了
zyiqibook 2015-03-05
  • 打赏
  • 举报
回复
shiro redis集群 示例项目源码贡献 ,抱歉,来晚了,因为我也涉及到了这一块,弄了这么一篇文章出来,项目拿来即用
gemcube 2015-01-17
  • 打赏
  • 举报
回复
别人要redis的,你给贴个zookeeper的,还是抄的不全
VIP_吴志国 2014-02-20
  • 打赏
  • 举报
回复
第一步:配置WEB.XML <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> 第二步:SHIRO整合SPRING配置 applicationContext-shiro.xml 伪代码: <!--Session集群配置--> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="globalSessionTimeout" value="3600000"/> <property name="sessionDAO" ref="zkShiroSessionDAO"/> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionIdCookie" ref="wapsession"/> </bean> <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID, 当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! --> <bean id="wapsession" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg name="name" value="WAPSESSIONID"/> </bean> <!-- 定时清理僵尸session,Shiro会启用一个后台守护线程定时执行清理操作 用户直接关闭浏览器造成的孤立会话 --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler"> <property name="interval" value="3600000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!--由zk做session存储容器--> <bean id="zkShiroSessionDAO" class="b2gonline.incometaxexamine._systembase.shiro.ZKShiroSessionDAO"> <!--使用内存缓存登录用户信息,一次获取用户登录信息后缓存到内存减少Shiro大量的读取操作,用户退出或超时后自动清除--> <constructor-arg name="useMemCache" value="true"/> <property name="zookeeperTemplate" ref="zookeeperTemplate"/> <property name="shiroSessionZKPath" value="/SHIROSESSIONS"/> <property name="sessionPrefix" value="session-"/> </bean> <!-- SHIRO安全接口 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> ... <property name="sessionManager" ref="sessionManager"/> </bean> 第三步:Zookeeper对Shiro-SessionDao实现类 ZKShiroSessionDAO.JAVA伪代码: import bgonline.foundation.hadoop.zk.IZookeeperTemplate; import bgonline.foundation.hadoop.zk.ZNode; import org.apache.shiro.cache.AbstractCacheManager; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheException; import org.apache.shiro.cache.MapCache; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.eis.AbstractSessionDAO; import org.apache.shiro.session.mgt.eis.CachingSessionDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.SerializationUtils; import java.io.Serializable; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * ZOOKEEPER实现SHIRO集群SESSION存储 * * @author aliencode * @date 13-7-10 */ public class ZKShiroSessionDAO extends CachingSessionDAO { public ZKShiroSessionDAO() { } private boolean useMemCache = false; /** * SESSION ZK DAO 实例 * 如果开户缓存 * 用户登录时自动缓存, 用户登录超时自动删除 * 由于shiro的cacheManager是全局的, 所以这里使用setActiveSessionsCache直接设置Cache来本地缓存, 而不使用全局zk缓存. * 由于同一用户可能会被路由到不同服务器,所以在doReadSession方法里也做了缓存增加. * * @param useMemCache 是否使用内存缓存登录信息 */ public ZKShiroSessionDAO(boolean useMemCache) { this.useMemCache = useMemCache; if (useMemCache) { setActiveSessionsCache( new MapCache<>(this.ACTIVE_SESSION_CACHE_NAME, new ConcurrentHashMap<Serializable, Session>()) ); } } Logger logger = LoggerFactory.getLogger(this.getClass()); /** * ZK操作类 */ private IZookeeperTemplate zookeeperTemplate; /** * 缓存根路径, 结尾不加/ */ private String shiroSessionZKPath = "/SHIROSESSIONS"; /** * 缓存项前缀 */ private String sessionPrefix = "session-"; /** * 设置Shiro Session 前缀 默认 session- * * @param sessionPrefix */ public void setSessionPrefix(String sessionPrefix) { this.sessionPrefix = sessionPrefix; } public void setZookeeperTemplate(IZookeeperTemplate zookeeperTemplate) { this.zookeeperTemplate = zookeeperTemplate; } /** * 设置Shiro在ZK服务器存放根路径 * * @param shiroSessionZKPath 默认值:/SHIROSESSIONS/ */ public void setShiroSessionZKPath(String shiroSessionZKPath) { this.shiroSessionZKPath = shiroSessionZKPath; } /** * session更新 * * @param session * @throws UnknownSessionException */ @Override public void update(Session session) throws UnknownSessionException { if (session == null || session.getId() == null) { logger.error("session argument cannot be null."); } saveSession(session, "update"); } @Override protected void doUpdate(Session session) { } /** * session删除 * * @param session */ @Override public void delete(Session session) { if (session == null || session.getId() == null) { logger.error("session argument cannot be null."); } logger.debug("delete session for id: {}", session.getId()); zookeeperTemplate.deleteNode(getPath(session.getId())); if (useMemCache) { this.uncache(session); } } @Override protected void doDelete(Session session) { } /** * 获取当前活跃的session, 当前在线数量 * * @return */ @Override public Collection<Session> getActiveSessions() { ZNode zNode = new ZNode(); zNode.setPath(shiroSessionZKPath); Set<Session> sessions = new HashSet<Session>(); //读取所有SessionID , 返回形如: session-9e3b5707-fa80-4d32-a6c9-f1c3685263a5 List<String> ss = zookeeperTemplate.getChildren(zNode); for (String id : ss) { if (id.startsWith(sessionPrefix)) { String noPrefixId = id.replace(sessionPrefix, ""); Session session = doReadSession(noPrefixId); if (session != null) sessions.add(session); } } logger.debug("shiro getActiveSessions. size: {}", sessions.size()); return sessions; } /** * 创建session, 用户登录 * * @param session * @return */ @Override protected Serializable doCreate(Session session) { Serializable sessionId = this.generateSessionId(session); this.assignSessionId(session, sessionId); saveSession(session, "create"); return sessionId; } /** * session读取 * * @param id * @return */ @Override protected Session doReadSession(Serializable id) { if (id == null) { logger.error("id is null!"); return null; } logger.debug("doReadSession for path: {}", getPath(id)); Session session; byte[] byteData = zookeeperTemplate.getData(getPath(id)).getByteData(); if (byteData != null && byteData.length > 0) { session = (Session) SerializationUtils.deserialize(byteData); if (useMemCache) { this.cache(session, id); logger.debug("doReadSession for path: {}, add cached !", getPath(id)); } return session; } else { return null; } } /** * 生成全路径 * * @param sessID * @return */ private String getPath(Serializable sessID) { return shiroSessionZKPath + '/' + sessionPrefix + sessID.toString(); } /** * session读取或更新 * * @param session * @param act update/save */ private void saveSession(Session session, String act) { Serializable sessionId = session.getId(); ZNode sessionNode = new ZNode(); sessionNode.setByteData(SerializationUtils.serialize(session)); sessionNode.setPath(getPath(sessionId)); logger.debug("save session for id: {}, act: {}", sessionId, act); if (act == "update") zookeeperTemplate.setData(sessionNode); else zookeeperTemplate.createNode(sessionNode); } }
【资源说明】 1、该资源包括项目的全部源码,下载可以直接使用! 2、本项目适合作为计算机、数学、电子信息等专业的课程设计、期末大作业和毕设项目,作为参考资料学习借鉴。 3、本资源作为“参考资料”如果需要实现其他功能,需要能看懂代码,并且热爱钻研,自行调试。 基于ssm+shiro+redis+nginx tomcat服务器集群管理项目源码+项目说明.zip Introduction ==== 1.搭建一个最简洁,模块划分最明确的ssm+swargger+shiro+redis+nginx整合项目,采用maven作为构建工具,在有新项目开发时可以借助此demo快速构建项目
2.实现shiro的授权信息缓存到redis数据库,减少关系数据库访问压力
3.实现session共享到redis实现服务器集群方案
4.配置文档中包含丰富的注释,搭建思路清晰的ssm项目框架
5.项目中的所有细节都会按照企业级开发的标准,展示如何遵循代码规范以及类文件doc注释的编写。
6.采用RESTFul的controller接口,展示RESTFul风格的API编写(shiro基于url的权限拦截与RESTFul API兼容性不好,后期可能会改写shiro以匹配RESTFul)
7.Junit单元测试,展示如何正确的使用Junit单元测试验证自己接口代码的健壮性
涉及到的技术 ==== springmvc+spring+mybatis:轻量级敏捷开发框架
swargger:快速构建RestFul接口测试页面
shiro:Apache开源权限管理框架,包括登录验证,授权,加密,会话管理
redis:Nosql数据库,搭配shiro会话管理功能将session存入redis中,实现tomcat多服务器集群的session共享
nginx:反向代理服务器,用来调度多台tomcat
h2:内存数据库,用于测试
开发环境 ==== jdk1.8+mysql5.7.22+tomcat8.5.32+IDEA
项目部署 ==== 第一次部署项目
1.修改ssm-rs\resources目录下db.properties的数据库账号密码信息
2.启动redis服务端,修改ssm-rs\resources\spring-config目录下spring-shiro.xml中redis的连接信息,没设置密码的话auth留空
2.创建数据库train_db并执行根目录下的train_db.sql数据库脚本
3.进入到ssm-build目录下,执行clean install -Dmaven.test.skip=true,对整个项目进行构建
4.启动ssm-rs项目,浏览器访问http://localhost/ssm-rs/swagger-ui.html
项目模块 ==== ssm-build
项目聚合模块,可以进入该项目目录,对整个项目进行构建。
mvn clean install -Dmaven.test.skip=true
ssm-parent
父模块,其他模块会继承该模块,引入公共的依赖
ssm-model
模型层模块,提供各种POJO。包括与数据库表对应的模型、传输模型等。提供给service层(ssm-cs)、controller层(ssm-rs)。
ssm-commons
包含各种工具类
ssm-cs
service层和dao层,提供具体的业务逻辑和数据库访问,需要依赖ssm-model模块,并提供出来给ssm-rs模块调用
ssm-rs
controller层,提供RESTFul接口。

25,980

社区成员

发帖
与我相关
我的任务
社区描述
高性能WEB开发
社区管理员
  • 高性能WEB开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧