将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 |
文章图片
添加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】
文章图片
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配置
文章图片
配置ecache 这个地方,必须添加一个shiro-activeSessionCache 的配置,不然就缓存不到session的数据了。
测试登录
文章图片
参考文章 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
推荐阅读
- √|shiro教程(session管理)
- Shiro入门-session管理
- servlet总结|设置session失效时间(不使用框架)----使用shiro设置session失效时间(使用shiro框架)
- shiro|shiro中session实现的简单分析
- Shiro 设置session超时时间
- springboot+shiro入门学习(一)
- Shiro|shiro中的session 获取过期时间/设置过期时间
- Shiro 之Subject、SecurityManager、Realm源码分析
- spring+shiro 整合之自己注册会话和自写realm