並不複雜,只需4步搞定Shiro集成redis實現會話共享

集羣的分佈式場景中,我們需要把衆多服務的會話狀態保持一致,常見的就是把會話信息保存到redis中實現共享,那麼你知道shiro集成redis實現會話共享有多簡單嗎?真的只需要4步!


在一些分佈式場景中,比如一個簡單負載均衡場景,一個nginx,反向代理到兩個tomcat,tomcat運行這同樣的項目,那麼這時候,服務的會話需要共享,因爲我們已經使用了shiro來完成我們的認證授權邏輯,那麼shiro完成登錄之後,如何讓另外一個服務同時也是登錄狀態呢?

我們延用上篇文章的項目代碼,使用兩個端口8080、8081分別啓動項目。

  • http://localhost:8080

  • http://localhost:8081

有些同學不知道怎麼用idea同一個項目分別啓動兩個端口,其實很簡單,只需要在 Run/DebugConfigurations的 VM options上,指定啓動端口 -Dserver.port=8081即可! 

ok,分別啓動8080和8081項目之後,登錄8080項目,然後訪問8081,發現8081未登錄!圖示如下:

那麼,如何才能8080登錄之後,8081也同時完成登錄呢?

特殊解決方式

其實在負載均衡集羣中,有些人是這樣解決問題,給ip指定服務,比如某個用戶請求經過nginx反向代理到8080服務,那麼nginx上指定 ip_hash:依據ip分配方式,那麼這個用戶就一直訪問同一個服務,不會訪問到8081服務,所以用戶就一直是訪問統一服務,所以在用戶看來,他就一直是登錄狀態的。

但是,這是有缺陷的,因爲ip和服務綁定了,加入這個服務掛了之後,是不會轉發到其他服務,所以對這用戶來說,就訪問異常。

所以我們需要使用更常用的負載均衡策略,比如輪詢、權重等。

回顧shiro的架構與組件

ok,進入正題,shiro做會話共享,會話信息可以存儲到內存,數據庫,或者緩存中間件中,這裏我們使用一個常用的緩存中間件Redis來保存我們的會話信息,那麼,我們就需要shiro集成redis。

可以回顧一下之前我們介紹過的shiro的整體架構: 

上面與會話或緩存相關的組件有:

  • Session Manager 會話管理器

  • Session DAO 會話 DAO,將session保存到數據庫、緩存等

  • Cache Manager 緩存管理器,權限認證的緩存、用戶及權限信息的緩存等

如果只是做會話共享,只是改寫Session DAO好像也是可以的,我之前試過,不過既然shiro已經集成redis,那麼數據啥的最好也一起共享吧,防止出現緩存不一致的情況。按照這個邏輯,其實就是重寫這3個組件就行了。

shiro-redis整合步驟

那麼,有什麼已經寫好的shiro集成redis整合項目嘛?

這裏給大家介紹一個

1.  `https://github.com/alexxiyang/shiro-redis/`

2.  `# 文檔`

3.  `https://github.com/alexxiyang/shiro-redis/blob/master/docs/README.md`

4.  `# 或者`

5.  `http://alexxiyang.github.io/shiro-redis/`

readme.md上有詳細的教程,這裏我們直接使用 Springboot starter方式,因爲需要寫的代碼少,直接寫寫配置就好。

第一步:導入shiro-redis的starter包


1.  `<dependency>`

2.  `<groupId>org.crazycake</groupId>`

3.  `<artifactId>shiro-redis-spring-boot-starter</artifactId>`

4.  `<version>3.2.1</version>`

5.  `</dependency>`

可以看下 shiro-redis-spring-boot-starter源碼:

可以看出,已經包含了 shiro-spring-boot-web-starter與 shiro-redis整合包了,所以之前項目中整合進來的 shiro-spring-boot-web-starter可以註釋掉了。

第二步,根據說明,readme裏面說,如果你沒有自定義創建SessionManager or SessionsSecurityManager,那麼會自動給你創建,集成完成。但是我們自定義了realm,所以SecurityManager還是必須要自定義的。

  • com.markerhub.config.ShiroConfig
1.  `@Configuration`

2.  `public  class  ShiroConfig  {`

4.  `@Autowired`

5.  `RedisSessionDAO redisSessionDAO;`

7.  `@Autowired`

8.  `RedisCacheManager redisCacheManager;`

10.  `@Bean`

11.  `AccountRealm accountRealm()  {`

12.  `return  new  AccountRealm();`

13.  `}`

15.  `@Bean`

16.  `public  SessionManager sessionManager()  {`

17.  `DefaultWebSessionManager sessionManager =  new  DefaultWebSessionManager();`

19.  `// inject redisSessionDAO`

20.  `sessionManager.setSessionDAO(redisSessionDAO);`

21.  `return sessionManager;`

22.  `}`

24.  `@Bean`

25.  `public  DefaultWebSecurityManager securityManager(AccountRealm accountRealm,  SessionManager sessionManager)  {`

26.  `DefaultWebSecurityManager securityManager =  new  DefaultWebSecurityManager();`

27.  `securityManager.setRealm(accountRealm);`

29.  `//inject sessionManager`

30.  `securityManager.setSessionManager(sessionManager);`

32.  `// inject redisCacheManager`

33.  `securityManager.setCacheManager(redisCacheManager);`

35.  `return securityManager;`

36.  `}`

38.  `@Bean`

39.  `public  ShiroFilterChainDefinition shiroFilterChainDefinition()  {`

40.  `DefaultShiroFilterChainDefinition chainDefinition =  new  DefaultShiroFilterChainDefinition();`

42.  `// logged in users with the 'admin' role`

43.  `chainDefinition.addPathDefinition("/admin/**",  "authc, roles[admin]");`

45.  `// logged in users with the 'document:read' permission`

46.  `chainDefinition.addPathDefinition("/docs/**",  "authc, perms[document:read]");`

48.  `chainDefinition.addPathDefinition("/login",  "anon");`

49.  `chainDefinition.addPathDefinition("/doLogin",  "anon");`

51.  `// all other paths require a logged in user`

52.  `chainDefinition.addPathDefinition("/**",  "authc");`

53.  `return chainDefinition;`

54.  `}`

55.  `}`

相比之前的配置,多了配置了個SessionManager,然後對應配置中注入sessionManager和redisCacheManager。好像項目就已經可以運行啦。

登錄成功之後查看redis中的數據效果如下: image

說明我們的會話信息已經保存到redis中啦。然後我們再換個端口8081啓動項目,發現8080登錄成功之後,8081服務也是登錄成功狀態。 image

第三步、根據需求調整參數,可以在配置文件中調整的參數我貼出來: image

  • application.yml

1.  `shiro-redis:`

2.  `enabled:  true`

3.  `redis-manager:`

4.  `host:  127.0.0.1:6379`

ok,shiro-redis已經整合完畢,是不是挺簡單的哈。

注意點

使用這個整合包,有個注意點,如果你使用了spring-boot-devtools作爲自動熱加載重啓,那麼自動重啓後會報錯,解決方法也簡單,官方已經給出瞭解決方案:

If you are using shiro-redis with spring-boot-devtools. Please add this line to resources/META-INF/spring-devtools.properties (Create it if there is no this file):

restart.include.shiro-redis=/shiro-[\w-.]+jar

所以,第四步:只需要在resources新建一個文件夾META-INF,然後新建文件spring-devtools.properties,內容爲:

  1. restart.include.shiro-redis=/shiro-[\\w-\\.]+jar

效果如下: 

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