集羣的分佈式場景中,我們需要把衆多服務的會話狀態保持一致,常見的就是把會話信息保存到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中的數據效果如下:
說明我們的會話信息已經保存到redis中啦。然後我們再換個端口8081啓動項目,發現8080登錄成功之後,8081服務也是登錄成功狀態。
第三步、根據需求調整參數,可以在配置文件中調整的參數我貼出來:
- 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,內容爲:
restart.include.shiro-redis=/shiro-[\\w-\\.]+jar
效果如下: