shiro源码篇 - shiro的session共享,你值得拥有

前言开心一刻老师对小明说:"乳就是小的意思,比如乳猪就是小猪,乳名就是小名,请你用乳字造个句"小明:"我家很穷,只能住在40平米的乳房"老师:"...,这个不行,换一个"小明:"我每天上学都要跳过我家门口的一条乳沟"老师:"......,这个也不行,再换一个"小明:"老师,我想不出来了,把我的乳头都想破了!"路漫漫其修远兮,吾将上下而求索!github:https://github.com/you...

shiro源码篇 - shiro的session共享,你值得拥有

前言

  开心一刻

    老师对小明说:"乳就是小的意思,比如乳猪就是小猪,乳名就是小名,请你用乳字造个句"
    小明:"我家很穷,只能住在40平米的乳房"
    老师:"..., 这个不行,换一个"
    小明:"我每天上学都要跳过我家门口的一条乳沟"
    老师:"......, 这个也不行,再换一个"
    小明:"老师,我想不出来了,把我的乳头都想破了!"

  路漫漫其修远兮,吾将上下而求索!

  github:https://github.com/youzhibing

  码云(gitee):https://gitee.com/youzhibing

前情回顾

  shiro的session创建session的查询、更新、过期、删除中,shiro对session的操作基本都讲到了,但还缺一个session共享没有讲解;session共享的原理其实在定义session管理一文已经讲过了,本文不讲原理,只看看shiro的session共享的实现。

  为何需要session共享

    如果是单机应用,那么谈不上session共享,session放哪都无所谓,不在乎放到默认的servlet容器中,还是抽出来放到单独的地方;

    也就是说session共享是针对集群(或分布式、或分布式集群)的;如果不做session共享,仍然采用默认的方式(session存放到默认的servlet容器),当我们的应用是以集群的方式发布的时候,同个用户的请求会被分发到不同的集群节点(分发依赖具体的负载均衡规则),那么每个处理同个用户请求的节点都会重新生成该用户的session,这些session之间是毫无关联的。那么同个用户的请求会被当成多个不同用户的请求,这肯定是不行的。

  如何实现session共享

    实现方式其实有很多,甚至可以不做session共享,具体有哪些,大家自行去查资料。本文提供一种方式:redis实现session共享,就是将session从servlet容器抽出来,放到redis中存储,所有集群节点都从redis中对session进行操作。

SessionDAO

  SessionDAO其实是用于session持久化的,但里面有缓存部分,具体细节我们往下看

  shiro已有SessionDAO的实现如下

  SessionDAO接口提供的方法如下

package org.apache.shiro.session.mgt.eis;import org.apache.shiro.session.Session;import org.apache.shiro.session.UnknownSessionException;import java.io.Serializable;import java.util.Collection;/** * 从EIS操作session的规范(EIS:例如关系型数据库, 文件系统, 持久化缓存等等, 具体依赖DAO实现) * 提供了典型的CRUD的方法:create, readSession, update, delete */public interface SessionDAO { /**  * 插入一个新的sesion记录到EIS*/ Serializable create(Session session); /**  * 根据会话ID获取会话  */ Session readSession(Serializable sessionId) throws UnknownSessionException; /**  * 更新session; 如更新session最后访问时间/停止会话/设置超时时间/设置移除属性等会调用  */ void update(Session session) throws UnknownSessionException; /**  * 删除session; 当会话过期/会话停止(如用户退出时)会调用  */ void delete(Session session); /**  * 获取当前所有活跃session, 所有状态不是stopped/expired的session  * 如果用户量多此方法影响性能  */ Collection<Session> getActiveSessions();}
View Code

    SessionDAO给出了从持久层(一般而言是关系型数据库)操作session的标准。

  AbstractSessionDAO提供了SessionDAO的基本实现,如下

package org.apache.shiro.session.mgt.eis;import org.apache.shiro.session.Session;import org.apache.shiro.session.UnknownSessionException;import org.apache.shiro.session.mgt.SimpleSession;import java.io.Serializable;/** * SessionDAO的抽象实现, 在会话创建和读取时做一些健全性检查,并在需要时允许可插入的会话ID生成策略. * SessionDAO的update和delete则留给子类来实现 * EIS需要子类自己实现 */public abstract class AbstractSessionDAO implements SessionDAO { /**  * sessionId生成器  */ private SessionIdGenerator sessionIdGenerator; public AbstractSessionDAO() {  this.sessionIdGenerator = new JavaUuidSessionIdGenerator(); // 指定JavaUuidSessionIdGenerator为默认sessionId生成器 } /**  * 获取sessionId生成器  */ public SessionIdGenerator getSessionIdGenerator() {  return sessionIdGenerator; } /**  * 设置sessionId生成器  */ public void setSessionIdGenerator(SessionIdGenerator sessionIdGenerator) {  this.sessionIdGenerator = sessionIdGenerator; } /**  * 生成一个新的sessionId, 并将它应用到session实例  */ protected Serializable generateSessionId(Session session) {  if (this.sessionIdGenerator == null) {String msg = "sessionIdGenerator attribute has not been configured.";throw new IllegalStateException(msg);  }  return this.sessionIdGenerator.generateId(session); } /**  * SessionDAO中create实现; 将创建的sesion保存到EIS.  * 子类doCreate方法的代理,具体的细节委托给了子类的doCreate方法  */ public Serializable create(Session session) {  Serializable sessionId = doCreate(session);  verifySessionId(sessionId);  return sessionId; } /**  * 保证从doCreate返回的sessionId不是null,并且不是已经存在的.  * 目前只实现了null校验,是否已存在是没有校验的,可能shiro的开发者会在后续补上吧.  */ private void verifySessionId(Serializable sessionId) {  if (sessionId == null) {String msg = "sessionId returned from doCreate implementation is null.  Please verify the implementation.";throw new IllegalStateException(msg);  } } /**  * 分配sessionId给session实例  */ protected void assignSessionId(Session session, Serializable sessionId) {  ((SimpleSession) session).setId(sessionId); } /**  * 子类通过实现此方法来持久化Session实例到EIS.  */ protected abstract Serializable doCreate(Session session); /**  * SessionDAO中readSession实现; 通过sessionId从EIS获取session对象.  * 子类doReadSession方法的代理,具体的获取细节委托给了子类的doReadSession方法.  */ public Session readSession(Serializable sessionId) throws UnknownSessionExceptio
源文地址:https://www.guoxiongfei.cn/cntech/2851.html