1. 程式人生 > >Apache Shiro(五)——Shiro會話管理

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;
    }

相關文章

shiro框架—多專案登入訪問共享session的實現