個人公衆號: MarkerHub,Java網站: https://markerhub.com更多精選文章請點擊:Java筆記大全.mdjava
集羣的分佈式場景中,咱們須要把衆多服務的會話狀態保持一致,常見的就是把會話信息保存到redis中實現共享,那麼你知道shiro集成redis實現會話共享有多簡單嗎?真的只須要4步!nginx
在一些分佈式場景中,好比一個簡單負載均衡場景,一個nginx,反向代理到兩個tomcat,tomcat運行這一樣的項目,那麼這時候,服務的會話須要共享,由於咱們已經使用了shiro來完成咱們的認證受權邏輯,那麼shiro完成登陸以後,如何讓另一個服務同時也是登陸狀態呢?git
咱們延用上篇文章的項目代碼,使用兩個端口8080、8081分別啓動項目。github
有些同窗不知道怎麼用idea同一個項目分別啓動兩個端口,其實很簡單,只須要在Run/Debug Configurations
的VM options
上,指定啓動端口-Dserver.port=8081
便可!
web
ok,分別啓動8080和8081項目以後,登陸8080項目,而後訪問8081,發現8081未登陸!圖示以下:redis
那麼,如何才能8080登陸以後,8081也同時完成登陸呢?spring
其實在負載均衡集羣中,有些人是這樣解決問題,給ip指定服務,好比某個用戶請求通過nginx反向代理到8080服務,那麼nginx上指定ip_hash:依據ip分配方式
,那麼這個用戶就一直訪問同一個服務,不會訪問到8081服務,因此用戶就一直是訪問統一服務,因此在用戶看來,他就一直是登陸狀態的。數據庫
可是,這是有缺陷的,由於ip和服務綁定了,加入這個服務掛了以後,是不會轉發到其餘服務,因此對這用戶來講,就訪問異常。segmentfault
因此咱們須要使用更經常使用的負載均衡策略,好比輪詢、權重等。緩存
ok,進入正題,shiro作會話共享,會話信息能夠存儲到內存,數據庫,或者緩存中間件中,這裏咱們使用一個經常使用的緩存中間件Redis來保存咱們的會話信息,那麼,咱們就須要shiro集成redis。
能夠回顧一下以前咱們介紹過的shiro的總體架構:
上面與會話或緩存相關的組件有:
若是隻是作會話共享,只是改寫Session DAO好像也是能夠的,我以前試過,不過既然shiro已經集成redis,那麼數據啥的最好也一塊兒共享吧,防止出現緩存不一致的狀況。按照這個邏輯,其實就是重寫這3個組件就好了。
那麼,有什麼已經寫好的shiro集成redis整合項目嘛?
這裏給你們介紹一個
https://github.com/alexxiyang/shiro-redis/ # 文檔 https://github.com/alexxiyang/shiro-redis/blob/master/docs/README.md # 或者 http://alexxiyang.github.io/shiro-redis/
readme.md上有詳細的教程,這裏咱們直接使用Spring boot starter
方式,由於須要寫的代碼少,直接寫寫配置就好。
第一步:導入shiro-redis的starter包
<dependency> <groupId>org.crazycake</groupId> <artifactId>shiro-redis-spring-boot-starter</artifactId> <version>3.2.1</version> </dependency>
能夠看下shiro-redis-spring-boot-starter
源碼:
能夠看出,已經包含了shiro-spring-boot-web-starter
與shiro-redis
整合包了,因此以前項目中整合進來的shiro-spring-boot-web-starter
能夠註釋掉了。
第二步,根聽說明,readme裏面說,若是你沒有自定義建立SessionManager or SessionsSecurityManager,那麼會自動給你建立,集成完成。可是咱們自定義了realm,因此SecurityManager仍是必需要自定義的。
@Configuration public class ShiroConfig { @Autowired RedisSessionDAO redisSessionDAO; @Autowired RedisCacheManager redisCacheManager; @Bean AccountRealm accountRealm() { return new AccountRealm(); } @Bean public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // inject redisSessionDAO sessionManager.setSessionDAO(redisSessionDAO); return sessionManager; } @Bean public DefaultWebSecurityManager securityManager(AccountRealm accountRealm, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(accountRealm); //inject sessionManager securityManager.setSessionManager(sessionManager); // inject redisCacheManager securityManager.setCacheManager(redisCacheManager); return securityManager; } @Bean public ShiroFilterChainDefinition shiroFilterChainDefinition() { DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); // logged in users with the 'admin' role chainDefinition.addPathDefinition("/admin/**", "authc, roles[admin]"); // logged in users with the 'document:read' permission chainDefinition.addPathDefinition("/docs/**", "authc, perms[document:read]"); chainDefinition.addPathDefinition("/login", "anon"); chainDefinition.addPathDefinition("/doLogin", "anon"); // all other paths require a logged in user chainDefinition.addPathDefinition("/**", "authc"); return chainDefinition; } }
相比以前的配置,多了配置了個SessionManager,而後對應配置中注入sessionManager和redisCacheManager。好像項目就已經能夠運行啦。
登陸成功以後查看redis中的數據效果以下:
說明咱們的會話信息已經保存到redis中啦。而後咱們再換個端口8081啓動項目,發現8080登陸成功以後,8081服務也是登陸成功狀態。
第三步、根據需求調整參數,能夠在配置文件中調整的參數我貼出來:
shiro-redis: enabled: true redis-manager: 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
效果以下:
好了,今天先講到這裏吧,shiro系列的文章已經發了幾篇了,後面還有一篇集成單點登陸的,而後就完結了哈。這裏是MarkerHub,我是小Hub呂一明,快去星標置頂個人公衆號吧!
(完)