Spring Shiro 使用默認的Session會話管理

項目中用到了shiro session會話管理機制,今天來總結一下,如下都是在spring boot框架實現。java

shiro的session管理機制很完善,也是獨立於j2ee容器且不依賴的,因此咱們徹底可使用shiro提供給咱們的session會話管理來實現咱們的業務邏輯,默認提供的sessionDAO是memorySessionDAO,這裏也主要講它的配置和原理。redis

首先來看下如何在spring boot下配置默認的session會話管理:spring

ShiroConfig類:緩存

@Configuration
public class ShiroConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;

    @Value("${spring.cache.type}")
    private String cacheType ;

    @Value("${server.session-timeout}")
    private int tomcatTimeout;

    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //設置realm.
        securityManager.setRealm(userRealm());
        // 自定義緩存實現 使用redis
        if (Constant.CACHE_TYPE_REDIS.equals(cacheType)) {
            securityManager.setCacheManager(rediscacheManager());
        } else {
            securityManager.setCacheManager(ehCacheManager());
        }
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    @Bean
    UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }


    @Bean
    public SessionDAO sessionDAO() {
            return new MemorySessionDAO();//使用默認的MemorySessionDAO
    }

    /**
     * shiro session的管理
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {  //配置默認的sesssion管理器
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000);
        sessionManager.setSessionDAO(sessionDAO());
        Collection<SessionListener> listeners = new ArrayList<SessionListener>();
        listeners.add(new BDSessionListener());
        sessionManager.setSessionListeners(listeners);
        return sessionManager;
    }

    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager em = new EhCacheManager();
        em.setCacheManager(cacheManager());
        return em;
    }

    @Bean("cacheManager2")
    CacheManager cacheManager(){
        return CacheManager.create();
    }


}

DefaultWebSessionManager是咱們配置管理器的核心類,裏面配置了session的超時時間,sessionDAO和監聽器。tomcat

在上面咱們向spring boot注入了:session

@Bean
    public SessionDAO sessionDAO() {
            return new MemorySessionDAO();//使用默認的MemorySessionDAO
    }

  MemorySessionDAO是shiro已經實現的Session的CRUD實現類,包括對session的操做,增刪改讀。框架

  下面來看下MemorySessionDAO的源碼實現:ide

public class MemorySessionDAO extends AbstractSessionDAO {
    private static final Logger log = LoggerFactory.getLogger(MemorySessionDAO.class);
    private ConcurrentMap<Serializable, Session> sessions = new ConcurrentHashMap();

    public MemorySessionDAO() {
    }

    protected Serializable doCreate(Session session) {
        Serializable sessionId = this.generateSessionId(session);
        this.assignSessionId(session, sessionId);
        this.storeSession(sessionId, session);
        return sessionId;
    }

    protected Session storeSession(Serializable id, Session session) {
        if (id == null) {
            throw new NullPointerException("id argument cannot be null.");
        } else {
            return (Session)this.sessions.putIfAbsent(id, session);
        }
    }

    protected Session doReadSession(Serializable sessionId) {
        return (Session)this.sessions.get(sessionId);
    }

    public void update(Session session) throws UnknownSessionException {
        this.storeSession(session.getId(), session);
    }

    public void delete(Session session) {
        if (session == null) {
            throw new NullPointerException("session argument cannot be null.");
        } else {
            Serializable id = session.getId();
            if (id != null) {
                this.sessions.remove(id);
            }

        }
    }

    public Collection<Session> getActiveSessions() {
        Collection<Session> values = this.sessions.values();
        return (Collection)(CollectionUtils.isEmpty(values) ? Collections.emptySet() : Collections.unmodifiableCollection(values));
    }
}

 在源碼中咱們能看到MemorySessionDAO繼承了AbstractSessionDAO類,這是一個抽象類,裏面實現了一部分方法,而AbstractSessionDAO又實現了SessionDAO接口,這纔是頂級接口,裏面提供了5個方法:this

public interface SessionDAO {
    Serializable create(Session var1);

    Session readSession(Serializable var1) throws UnknownSessionException;

    void update(Session var1) throws UnknownSessionException;

    void delete(Session var1);

    Collection<Session> getActiveSessions();
}

 這5個方法也是memorySessionDAO實現的5個方法。 code

 能夠看出配置很是簡單,咱們幾乎不須要作任何事情,下面來看看怎麼來使用吧,

 以一個SessionService爲例:

@Service
public interface SessionService {
	List<UserOnline> list(); //獲得系統在線用戶

	boolean forceLogout(String sessionId); //強制登出
}

裏面有兩個方法,分別是查詢系統當前在線的用戶,以及強制登出某個用戶。

實現以下:

@Service
public class SessionServiceImpl implements SessionService {
    private final SessionDAO sessionDAO;

    @Autowired
    public SessionServiceImpl(SessionDAO sessionDAO) {
        this.sessionDAO = sessionDAO;
    }

    @Override
    public List<UserOnline> list() {
        List<UserOnline> list = new ArrayList<>();
        Collection<Session> sessions = sessionDAO.getActiveSessions();
        for (Session session : sessions) {
            UserOnline userOnline = new UserOnline();
            if (session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY) == null) {
                continue;
            } else {
                SimplePrincipalCollection principalCollection = (SimplePrincipalCollection) session
                        .getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                UserDO userDO = (UserDO) principalCollection.getPrimaryPrincipal();
                userOnline.setUsername(userDO.getUsername());
            }
            userOnline.setId((String) session.getId());
            userOnline.setHost(session.getHost());
            userOnline.setStartTimestamp(session.getStartTimestamp());
            userOnline.setLastAccessTime(session.getLastAccessTime());
            userOnline.setTimeout(session.getTimeout());
            list.add(userOnline);
        }
        return list;
    }


    @Override
    public boolean forceLogout(String sessionId) {
        Session session = sessionDAO.readSession(sessionId);
        session.setTimeout(0);
        return true;
    }
}

在代碼中,咱們注入了shiro的SessionDAO,

 Collection<Session> sessions = sessionDAO.getActiveSessions();  能夠獲得當前在線的session集合。

 每個session都封裝了用戶的各類信息,

 userOnline.setId((String) session.getId()); //獲得session的ID
 userOnline.setHost(session.getHost()); //獲得用戶的主機
 userOnline.setStartTimestamp(session.getStartTimestamp()); //獲得用戶的登陸時間
 userOnline.setLastAccessTime(session.getLastAccessTime());//獲得用戶最後登陸時間
 userOnline.setTimeout(session.getTimeout());/獲得過時時間

 若是咱們想在強制讓某用戶登出系統,只須要session.setTimeout(0)便可。

 是否是以爲很是簡便,若是系統需求不高,咱們徹底可使用shiro爲咱們實現的這個sessionDAO,

而若是系統有定製需求,咱們也能夠實現自定義的sessionDAO,好比咱們能夠將session的CRUD經過redis來實現,一樣的繼承AbstractSessionDAO類,重寫它的方法便可,將session的各類存取修改以redis爲載體,基本的邏輯就是這樣,若是有問題歡迎在評論中留言。

相關文章
相關標籤/搜索