作者:whisper
链接:http://proprogrammar.com:443/article/113
声明:请尊重原作者的劳动,如需转载请注明出处
Shiro 提供了完整的企业级会话管理功能,不依赖于底层容器(如web容器tomcat),不管 JavaSE 还是 JavaEE 环境都可以使用,提供了会话管理、会话事件监听、会话存储/持久化、容器无关的集群、失效/过期支持、对Web 的透明支持、 SSO 单点登录的支持等特性。
会话相关的 API
Subject.getSession():即可获取会话;其等价于Subject.getSession(true),即如果当前没有创建 Session 对象会创建
一个;Subject.getSession(false),如果当前没有创建 Session 则返回null
session.getId():获取当前会话的唯一标识
session.getHost():获取当前Subject的主机地址
session.getTimeout() & session.setTimeout(毫秒):获取/设置当前Session的过期时间
session.getStartTimestamp() & session.getLastAccessTime():获取会话的启动时间及最后访问时间;如果是 JavaSE 应用需要自己定期调用 session.touch() 去更新最后访问时间;如果是 Web 应用,每次进入 ShiroFilter 都会自动调用 session.touch() 来更新最后访问时间。
session.touch() & session.stop():更新会话最后访问时间及销毁会话;当Subject.logout()时会自动调用 stop 方法
来销毁会话。如果在web中,调用 HttpSession. invalidate()也会自动调用Shiro Session.stop 方法进行销毁Shiro 的会话
session.setAttribute(key, val) &session.getAttribute(key) &session.removeAttribute(key):设置/获取/删除会话属性;在整个会话范围内都可以对这些属性进行操作
会话监听器
会话监听器用于监听会话创建、过期及停止事件,类似于HttpSessionListener
注意:shiro的session和HttpSession可以互相操作,如下图,controller层shiro的session设置了值,httpSession来读取,同时在service层,shiro的session读取httpSession的内容
结果如下,可以看到,都读取到了
SessionDao
可以将Session持久化到数据库
继承关系
AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话ID等
CachingSessionDAO 提供了对开发者透明的会话缓存的功能,需要设置相应的 CacheManager
MemorySessionDAO 直接在内存中进行会话维护
EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用ConcurrentHashMap 保存缓存的会话。
配置示例,其中sessionManager要配置给SecurityManager
数据表
sessionDAO的实现,实现了增删改查
package com.atguigu.shiro.realms;
import java.io.Serializable;
import java.util.List;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.ValidatingSession;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
public class MySessionDao extends EnterpriseCacheSessionDAO {
@Autowired
private JdbcTemplate jdbcTemplate = null;
@Override
protected Serializable doCreate(Session session) {
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();
}
@Override
protected Session doReadSession(Serializable sessionId) {
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));
}
@Override
protected void doUpdate(Session session) {
if (session instanceof ValidatingSession
&& !((ValidatingSession) session).isValid()) {
return;
}
String sql = "update sessions set session=? where id=?";
jdbcTemplate.update(sql, SerializableUtils.serialize(session),
session.getId());
}
@Override
protected void doDelete(Session session) {
String sql = "delete from sessions where id=?";
jdbcTemplate.update(sql, session.getId());
}
}
工具类SerializableUtils的实现
package com.atguigu.shiro.realms;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.Session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableUtils {
public static String serialize(Session session) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(session);
return Base64.encodeToString(bos.toByteArray());
} catch (Exception e) {
throw new RuntimeException("serialize session error", e);
}
}
public static Session deserialize(String sessionStr) {
try {
ByteArrayInputStream bis = new ByteArrayInputStream(
Base64.decode(sessionStr));
ObjectInputStream ois = new ObjectInputStream(bis);
return (Session) ois.readObject();
} catch (Exception e) {
throw new RuntimeException("deserialize session error", e);
}
}
}
会话验证
Shiro 提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话
出于性能考虑,一般情况下都是获取会话时来验证会话是否过期并停止会话的;但是如在 web 环境中,如果用户不主动退出是不知道会话是否过期的,因此需要定期的检测会话是否过期,Shiro 提供了会话验证调度器:SessionValidationScheduler
Shiro 也提供了使用Quartz会话验证调度器:QuartzSessionValidationScheduler
亲爱的读者:有时间可以点赞评论一下
全部评论