Shiro之保存Session到数据库中-yellowcong

将Session统一存放到Mysql数据库中进行管理操作,这样我们就可以通过向操作数据库一样,对session进行操作和处理了。实现Session存储到数据库的大致步骤是,1、创建Session表;2、创建操作Session表的Mapper,3、创建继承EnterpriseCacheSessionDAO 的Dao,4、配置管理session的Dao到securityManager中,5、配置ecache配置。
源码地址 https://gitee.com/yellowcong/shior-dmeo/tree/master/test
环境架构
服务 版本
数据库 Mysql
缓存 ehcache
框架 Spring+SpringMVC+Mybatis
项目结构 Shiro之保存Session到数据库中-yellowcong
文章图片

添加SessionDao 数据库表设计
数据表设计中,我们可以自己在我的基础上,进行字段的扩展
CREATE TABLE `sys_session` ( `id` varchar(200) NOT NULL COMMENT 'Sessoin的id', `session` varchar(2000) DEFAULT NULL COMMENT 'Session的序列化对象', `username` varchar(32) DEFAULT NULL COMMENT '用户名', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8

【Shiro之保存Session到数据库中-yellowcong】Shiro之保存Session到数据库中-yellowcong
文章图片

SessionMapper.xml
SELECT LAST_INSERT_ID() insert into sys_session (id,session) values (#{id,jdbcType=VARCHAR},#{session,jdbcType=VARCHAR}) delete from sys_session where id = #{sessionid,jdbcType=VARCHAR} update sys_session set session = #{session,jdbcType=VARCHAR} , username = #{username,jdbcType=VARCHAR} where id = #{id,jdbcType=VARCHAR}

SessionMapper.java
package com.yellowcong.shiro.dao; import java.util.List; import org.apache.ibatis.annotations.Param; import com.yellowcong.shiro.model.Session; /** * 创建日期:2017/12/21
* 创建时间:8:44:22
* 创建用户:yellowcong
* 机能概要: */ public interface SessionMapper {/** * 创建日期:2017/12/21
* 创建时间:8:44:54
* 创建用户:yellowcong
* 机能概要:插入session * * @param session */ public int insert(@Param("id") String id,@Param("session") String session); /** * 创建日期:2017/12/21
* 创建时间:8:48:06
* 创建用户:yellowcong
* 机能概要:删除session * * @param session * @return */ public int delete(Stringsessionid); /** * * 创建日期:2017/12/21
* 创建时间:8:48:23
* 创建用户:yellowcong
* 机能概要:删除session * * @param session * @return */ public int update(@Param("id") String id,@Param("session") String session,@Param("username") String username); /** * 创建日期:2017/12/21
* 创建时间:8:49:13
* 创建用户:yellowcong
* 机能概要:通过sessionid来获取session数据 * * @param sessionid * @return */ public Session load(String sessionid); /** * 创建日期:2017/12/21
* 创建时间:11:52:02
* 创建用户:yellowcong
* 机能概要:根据用户名获取sesssion * @param username * @return */ public List loadByUserName(@Param("username") String username); }

创建SessionDao,管理session
SessionDao需要继承EnterpriseCacheSessionDAO ,实现里面的抽象方法,同时,自己还添加了一个根据用户名来获取Session对象的方法。这个里面直接操作Session存储到数据库。
对于插入用户名,需要在update sessiond的地方做处理,不然获取不到用户名
package com.yellowcong.shiro.dao; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import org.apache.shiro.session.Session; import org.apache.shiro.session.UnknownSessionException; import org.apache.shiro.session.mgt.ValidatingSession; import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO; import org.apache.shiro.subject.support.DefaultSubjectContext; import org.springframework.beans.factory.annotation.Autowired; import com.yellowcong.shiro.utils.SerializableUtils; /** * 创建日期:2017/12/21
* 创建时间:8:31:04
* 创建用户:yellowcong
* 机能概要:用于Session的保存 */ public class SessionDao extends EnterpriseCacheSessionDAO {@Autowired private SessionMapper sessionMapper; public void delete(Session session) { //删除session this.sessionMapper.delete(session.getId().toString()); }public void update(Session session) throws UnknownSessionException { //当是ValidatingSession 无效的情况下,直接退出 if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid() ) { return ; }//检索到用户名 String username = String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY)); //序列化Session this.sessionMapper.update(session.getId().toString(), SerializableUtils.serializ(session),username); }@Override protected Serializable doCreate(Session session) { //生成session的id Serializable sessionId = generateSessionId(session); //给Session设定id assignSessionId(session, sessionId); //插入session 到数据库 this.sessionMapper.insert(session.getId().toString(), SerializableUtils.serializ(session)); return sessionId; }/** * 创建日期:2017/12/21
* 创建时间:13:56:15
* 创建用户:yellowcong
* 机能概要:通过名称来获取用户 Session * @param username * @return */ public List loadByUserName(String username) { //获取session的字符串 List dbSessions = this.sessionMapper.loadByUserName(username); //判断是否存在用户的情况 if(dbSessions == null || dbSessions.size() == 0) { return null; }List result = new ArrayList(); for(com.yellowcong.shiro.model.Session session:dbSessions) { //加载session数据 String sessionStr = session.getSession(); //将Session的数据串,转化为对象 result.add(SerializableUtils.deserializ(sessionStr)); }return result; }@Override protected Session doReadSession(Serializable sessionId) { //获取session的字符串 com.yellowcong.shiro.model.Session dbSession = this.sessionMapper.load(sessionId.toString()); if(dbSession == null) { return null; }//加载session数据 String sessionStr = dbSession.getSession(); return SerializableUtils.deserializ(sessionStr); }}

SerializableUtils
序列和反序列Session对象,只有将session对象序列化成字符串,才可以存储到Mysql上,不能直接存
package com.yellowcong.shiro.utils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Base64; import org.apache.shiro.session.Session; /** * 创建日期:2017/12/21
* 创建时间:9:21:25
* 创建用户:yellowcong
* 机能概要: */ public class SerializableUtils {/** * 创建日期:2017/12/21
* 创建时间:9:25:30
* 创建用户:yellowcong
* 机能概要:将Session序列化成String类型 * @param session * @return */ public static String serializ(Session session) { try { //ByteArrayOutputStream 用于存储序列化的Session对象 ByteArrayOutputStream bos = new ByteArrayOutputStream(); //将Object对象输出成byte数据 ObjectOutputStream out = new ObjectOutputStream(bos); out.writeObject(session); //将字节码,编码成String类型数据 return Base64.getEncoder().encodeToString(bos.toByteArray()); } catch (Exception e) { throw new RuntimeException("序列化失败"); } }/** * 创建日期:2017/12/21
* 创建时间:9:26:19
* 创建用户:yellowcong
* 机能概要:将一个Session的字符串序列化成字符串,反序列化 * @param sessionStr * @return */ public static Session deserializ(String sessionStr) { try { //读取字节码表 ByteArrayInputStream bis= new ByteArrayInputStream(Base64.getDecoder().decode(sessionStr)); //将字节码反序列化成 对象 ObjectInputStream in = new ObjectInputStream(bis); Session session = (Session) in.readObject(); return session; } catch (Exception e) { throw new RuntimeException("反序列化失败"); } } }

配置spring-shiro.xml配置文件 注意这个sessionDao 里面配置了activeSessionsCacheName 这个属性,这个在ecache.xml里面必须也配置一个shiro-activeSessionCache节点,用于存激活的session,简单来讲,就是登录的用户。

下面是完整配置 其中还有一部分是关于Shiro生命周期的,存储在了Spring-mvc中,因为生命周期配置在spring-shiro.xml中不生效
== Shiro Components ==

spring-mvc.xml的shiro配置

Shiro之保存Session到数据库中-yellowcong
文章图片

配置ecache 这个地方,必须添加一个shiro-activeSessionCache 的配置,不然就缓存不到session的数据了。

测试登录 Shiro之保存Session到数据库中-yellowcong
文章图片

参考文章 http://blog.csdn.net/qq_32347977/article/details/51084480
http://blog.csdn.net/lhacker/article/details/20444295
http://blog.csdn.net/lhacker/article/details/19340757

    推荐阅读