Apache Shiro(五)——Shiro會話管理
一、概述
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;
}