一、概述
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):设置/获取/删除会话属性;在整个会话范围内都可以对这些属性进行操作。
三、会话监听器
会话监听器用于监听会话创建、过期及停止事件。
package org.apache.shiro.session;
public interface SessionListener {
void onStart(Session session);
void onStop(Session session);
void onExpiration(Session session);
}
四、SessionDao
AbstractSessionDAO 提供了 SessionDAO 的基础实现,如生成会话ID等。
CachingSessionDAO 提供了对开发者透明的会话缓存的功能,需要设置相应的CacheManager。
MemorySessionDAO 直接在内存中进行会话维护。
EnterpriseCacheSessionDAO 提供了缓存功能的会话维护,默认情况下使用 MapCache 实现,内部使用ConcurrentHashMap 保存缓存的会话。
五、会话验证
Shiro 提供了会话验证调度器,用于定期的验证会话是否已过期,如果过期将停止会话。出于性能考虑,一般情况下都是获取会话时来验证会话是否过期并停止会话的;但是如在 web 环境中,如果用户不主动退出是不知道会话是否过期的,因此需要定期的检测会话是否过期,Shiro 提供了会话验证调度器SessionValidationScheduler。
Shiro 也提供了使用Quartz会话验证调度器:QuartzSessionValidationScheduler。
六、编写自定义SessionDao
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代码:
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);
}
}
}
七、更改ShiroConfig配置文件
@Bean
public JavaUuidSessionIdGenerator javaUuidSessionIdGenerator(){
return new JavaUuidSessionIdGenerator();
}
@Bean
public MySessionDao mySessionDao(){
return new MySessionDao();
}
/**
* session管理器
*/
@Bean
public DefaultWebSessionManager defaultWebSessionManager(CacheManager cacheShiroManager) {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
// 关键在这里
// MySessionDao 接入到shiro 中的sessionManager
sessionManager.setSessionDAO(mySessionDao());
sessionManager.setCacheManager(cacheShiroManager);
sessionManager.setSessionValidationInterval(sessionValidationInterval * 1000);
sessionManager.setGlobalSessionTimeout(sessionInvalidateTime * 1000);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
Cookie cookie = new SimpleCookie(ShiroHttpSession.DEFAULT_SESSION_ID_NAME);
cookie.setName("shiroCookie");
cookie.setHttpOnly(true);
sessionManager.setSessionIdCookie(cookie);
return sessionManager;
}
/**
* 安全管理器securityManager
*/
@Bean
public DefaultWebSecurityManager securityManager(CookieRememberMeManager rememberMeManager,
CacheManager cacheShiroManager,
SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(this.shiroRealm());
securityManager.setCacheManager(cacheShiroManager);
securityManager.setRememberMeManager(rememberMeManager);
// 将sessionManager 注入到securityManager
securityManager.setSessionManager(sessionManager);
return securityManager;
}