Shiro 提供SessionDAO 用于会话的CRUD,即DAO(Data Access Object)模式实现:
1. 如DefaultSessionManager 在创建完session 后会调用该方法;如保存到关系数据库/文件系统/NoSQL 数据库;即可以实现会话的持久化;返回会话ID;主要此处返回的ID.equals(session.getId());
Serializable create(Session session);
2. 根据会话ID获取会话
Session readSession(Serializable sessionId) throws UnknownSessionException;
3. 更新会话;如更新会话最后访问时间/停止会话/设置超时时间/设置移除属性等会调用
void update(Session session) throws UnknownSessionException;
4. 删除会话;当会话过期/会话停止(如用户退出时)会调用
void delete(Session session);
5. 获取当前所有活跃用户,如果用户量多此方法影响性能
Collection<Session> getActiveSessions();
Shiro 内嵌了如下SessionDAO 实现:
AbstractSessionDAO提供了SessionDAO的基础实现,如生成会话ID等;
CachingSessionDAO提供了对开发者透明的会话缓存的功能,只需要设置相应的CacheManager 即可;
MemorySessionDAO 直接在内存中进行会话维护;
EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用MapCache 实现,内部使用ConcurrentHashMap 保存缓存的会话。
可以通过如下配置设置SessionDAO:
sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionManager.sessionDAO=$sessionDAO
一、 Shiro 提供了使用Ehcache 进行会话存储,Ehcache 可以配合TerraCotta 实现容器无关的分布式集群。
首先在pom.xml里添加如下依赖:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
接着配置shiro-web.ini文件:
sessionDAO=org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO
sessionDAO.activeSessionsCacheName=shiro-activeSessionCache
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
sessionDAO.sessionIdGenerator=$sessionIdGenerator
sessionManager.sessionDAO=$sessionDAO
cacheManager=org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile=classpath:ehcache.xml
securityManager.cacheManager=$cacheManager
securityManager.sessionManager=$sessionManager
sessionDAO. activeSessionsCacheName : 设置Session 缓存名字, 默认就是shiro-activeSessionCache;
cacheManager:缓存管理器,用于管理缓存的,此处使用Ehcache实现;
cacheManager.cacheManagerConfigFile:设置ehcache缓存的配置文件;
securityManager.cacheManager:设置SecurityManager的cacheManager,会自动设置实现了CacheManagerAware接口的相应对象,如SessionDAO 的cacheManager;
然后配置ehcache.xml:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">
<!-- <diskStore path="java.io.tmpdir"/> -->
<cache name="shiro-activeSessionCache"
maxEntriesLocalHeap="10000"
overflowToDisk="false"
eternal="false"
diskPersistent="false"
timeToLiveSeconds="0"
timeToIdleSeconds="0"
statistics="true"/>
</ehcache>
Cache的名字为shiro-activeSessionCache,即设置的sessionDAO的activeSessionsCacheName属性值。
另外可以通过如下ini配置设置会话ID 生成器:
sessionIdGenerator=org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator
sessionDAO.sessionIdGenerator=$sessionIdGenerator
用于生成会话ID,默认就是JavaUuidSessionIdGenerator,使用java.util.UUID生成。
如果自定义实现SessionDAO,继承CachingSessionDAO 即可:
public class MySessionDao extends CachingSessionDAO{
private JdbcTemplate jdbcTemplate = JdbcTemplateUtils.jdbcTemplate();
protected Serializable doCreate(Session session) {
System.out.println("doCreate");
Serializable sessionId = generateSessionId(session);
assignSessionId(session, sessionId);
String sql = "insert into sessions (id,session) values (?,?)";
jdbcTemplate.update(sql, sessionId,SerializableUtils.serialize(session));
return session.getId();
}
protected void doUpdate(Session session) {
System.out.println("doUpdate");
if(session instanceof ValidatingSession && !((ValidatingSession)session).isValid()){
return;//如果会话过期/停止,没必要再更新了
}
String sql = "update sessions set session=? where id=?";
jdbcTemplate.update(sql,SerializableUtils.serialize(session),session.getId());
}
protected void doDelete(Session session) {
System.out.println("doDelete");
String sql="delete from sessions where id = ?";
jdbcTemplate.update(sql,session.getId());
}
protected Session doReadSession(Serializable sessionId) {
System.out.println("doReadSession");
String sql = "select session from sessions where id = ?";
List<String> sessionStrList = jdbcTemplate.queryForList(sql,String.class,sessionId);
if(sessionStrList.size()==0) return null;
return SerializableUtils.deserialize(sessionStrList.get(0));
}
}
doCreate/doUpdate/doDelete/doReadSession 分别代表创建/修改/删除/读取会话;此处通过把会话序列化后存储到数据库实现;接着在shiro-web.ini中配置:
sessionDAO=session.dao.MySessionDao
其他设置和之前一样,因为继承了CachingSessionDAO;所有在读取时会先查缓存中是否存在,如果找不到才到数据库中查找。