使用Shiro+Redis實現Session共享

1. 爲什麼要實現Session共享?

說到這個問題,我們先來了解一下負載均衡的概念吧

1.1 負載均衡

當一個項目的訪問量很大時,一個Tomcat喫不消,這時候就準備多個Tomcat,由Nginx按照權重來對請求進行分配,從而緩解單獨一個Tomcat受到的壓力。

如果想進一步瞭解的話,可以訪問網站:Nginx 負載均衡

1.2 負載均衡中的Session問題

通過負載均衡,我們可以把請求分發到不同的 Tomcat 來緩解服務器的壓力。但這存在一個問題:當同一個用戶第一次訪問tomcat:8080,並且登錄成功;接着第二次訪問時,卻被分配到了tomcat:8081, 這裏並沒有記錄他的登陸狀態,那麼就會呈現未登錄狀態了,嚴重影響了用戶體驗。

1.3 案例演示

或許這樣說,比較抽象,就拿這篇博客-------一步一步地教你使用SpringBoot集成Shiro的代碼演示吧。

在IDEA中,使用8080和8081端口啓動項目。
對同一個SpringBoot項目使用不同端口啓動,只需要配置VM參數即可:

D.server.port=端口號

按如下步驟:
1)、Run -> Edit Configurations
在這裏插入圖片描述
2)、填寫VM參數
在這裏插入圖片描述
3)、點擊左上角“+”,添加一個新的端口號
在這裏插入圖片描述
4)、填寫名稱,選擇主類,填寫VM參數
在這裏插入圖片描述
分別啓動這兩個端口的項目。

  1. 使用8080端口登錄:http://localhost:8080/login
  2. 登錄成功後,使用8081端口訪問登錄成功界面:http://localhost:8081/success
  3. 發現8081端口又跳轉到了login頁面,說明8081端口未登錄

上面的問題:我在8080端口登錄成功,訪問8081端口,發現它沒有登錄。這就出現了上面的Session問題了。

那麼,如何實現8080端口登錄成功,8081端口也同時完成登錄呢?

解決方法:

  1. ip_hash:通過ip地址標記用戶,如果多次請求都是從同一個ip來的,那麼就都分配到同一個tomcat。但這也有缺陷:如果tomcat:8080掛了,那麼此時nginx只能把請求交給tomcat:8081,但是這裏卻沒有記錄session,用戶體驗依然受影響
  2. 使用redis:當tomcat:8080需要保存session值的時候,就可以把它放在Redis上,需要取的時候,也從Redis上取

2. Shiro架構

shiro的整體架構:
在這裏插入圖片描述
上面與會話或緩存相關的組件有:

  • Session Manager:會話管理器
  • Session DAO:會話 DAO,將session保存到數據庫、緩存等
  • Cache Manager:緩存管理器,權限認證的緩存、用戶及權限信息的緩存等

如果只是做Session共享,只需添加SessionDAO即可。不過爲了防止出現緩存不一致性的問題,也將數據一起共享。所以,需要添加以上三個組件。

3. Shiro集成Redis

可查看:shiro-redis整合文檔

在這篇博客----一步一步地教你使用SpringBoot集成Shiro 代碼的基礎上進行修改

1)、POM依賴

<dependency>
    <groupId>org.crazycake</groupId>
    <artifactId>shiro-redis</artifactId>
    <version>3.2.3</version>
</dependency>

2)、ShiroConfig
添加四個Bean

@Bean
public RedisManager redisManager() {
	// 它可對redis的ip、端口等進行配置
    RedisManager redisManager = new RedisManager();
    return redisManager;
}

@Bean
public SessionDAO sessionDAO() {
    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    // session在redis中的保存時間,最好大於session會話超時時間
    redisSessionDAO.setExpire(100);
    return redisSessionDAO;
}

@Bean
public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    //redis中針對不同用戶緩存
    redisCacheManager.setPrincipalIdFieldName("id");
    //用戶權限信息緩存時間
    redisCacheManager.setExpire(200);
    return redisCacheManager;
}

@Bean
public SessionManager sessionManager() {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setSessionDAO(sessionDAO());
    sessionManager.setCacheManager(cacheManager());
    // 局會話超時時間(單位毫秒),默認30分鐘
    sessionManager.setGlobalSessionTimeout(10000);
    // 取消登錄成功後url 後面的 JSESSIONID
    sessionManager.setSessionIdUrlRewritingEnabled(false);
    return sessionManager;
}

修改SecurityManager

@Bean
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(userRealm());
    securityManager.setSessionManager(sessionManager());
    securityManager.setCacheManager(cacheManager());
    return securityManager;
}

3)、User類要實現Serializable接口,否則啓動報錯
4)、先啓動redis,再啓動兩個端口項目
5)、使用8080端口登錄,使用8081端口訪問
8080端口:localhost:8080/login
在這裏插入圖片描述
8080端口登錄成功
在這裏插入圖片描述
redis中也是有數據的

8081端口:http://localhost:8081/success
在這裏插入圖片描述
可直接訪問,並訪問成功。

好了,就這樣實現了一個簡單的Session共享了。

【參考文章】
SpringBoot shiro集成redis實現session共享

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章