成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

集群化部署,Spring Security 要如何處理 Session 共享?

開發 架構
大家聊了 Spring Security 如何像 QQ 一樣,自動踢掉已登錄用戶,但是前面我們是基于單體應用的,如果我們的項目是集群化部署,這個問題該如何解決呢?

大家聊了 Spring Security 如何像 QQ 一樣,自動踢掉已登錄用戶,但是前面我們是基于單體應用的,如果我們的項目是集群化部署,這個問題該如何解決呢?

[[326496]]

今天我們就來看看集群化部署,Spring Security 要如何處理 session 并發。

1.集群會話方案

在傳統的單服務架構中,一般來說,只有一個服務器,那么不存在 Session 共享問題,但是在分布式/集群項目中,Session 共享則是一個必須面對的問題,先看一個簡單的架構圖:

 

在這樣的架構中,會出現一些單服務中不存在的問題,例如客戶端發起一個請求,這個請求到達 Nginx 上之后,被 Nginx 轉發到 Tomcat A 上,然后在 Tomcat A 上往 session 中保存了一份數據,下次又來一個請求,這個請求被轉發到 Tomcat B 上,此時再去 Session 中獲取數據,發現沒有之前的數據。

1.1 session 共享

對于這一類問題的解決,目前比較主流的方案就是將各個服務之間需要共享的數據,保存到一個公共的地方(主流方案就是 Redis):

 

當所有 Tomcat 需要往 Session 中寫數據時,都往 Redis 中寫,當所有 Tomcat 需要讀數據時,都從 Redis 中讀。這樣,不同的服務就可以使用相同的 Session 數據了。

這樣的方案,可以由開發者手動實現,即手動往 Redis 中存儲數據,手動從 Redis 中讀取數據,相當于使用一些 Redis 客戶端工具來實現這樣的功能,毫無疑問,手動實現工作量還是蠻大的。

一個簡化的方案就是使用 Spring Session 來實現這一功能,Spring Session 就是使用 Spring 中的代理過濾器,將所有的 Session 操作攔截下來,自動的將數據 同步到 Redis 中,或者自動的從 Redis 中讀取數據。

對于開發者來說,所有關于 Session 同步的操作都是透明的,開發者使用 Spring Session,一旦配置完成后,具體的用法就像使用一個普通的 Session 一樣。

1.2 session 拷貝

session 拷貝就是不利用 redis,直接在各個 Tomcat 之間進行 session 數據拷貝,但是這種方式效率有點低,Tomcat A、B、C 中任意一個的 session 發生了變化,都需要拷貝到其他 Tomcat 上,如果集群中的服務器數量特別多的話,這種方式不僅效率低,還會有很嚴重的延遲。

所以這種方案一般作為了解即可。

1.3 粘滯會話

所謂的粘滯會話就是將相同 IP 發送來的請求,通過 Nginx 路由到同一個 Tomcat 上去,這樣就不用進行 session 共享與同步了。這是一個辦法,但是在一些極端情況下,可能會導致負載失衡(因為大部分情況下,都是很多人用同一個公網 IP)。

所以,Session 共享就成為了這個問題目前主流的解決方案了。

2.Session共享

2.1 創建工程

首先 創建一個 Spring Boot 工程,引入 Web、Spring Session、Spring Security 以及 Redis:

 

創建成功之后,pom.xml 文件如下:

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-data-redis</artifactId> 
  4. </dependency> 
  5. <dependency> 
  6.     <groupId>org.springframework.boot</groupId> 
  7.     <artifactId>spring-boot-starter-security</artifactId> 
  8. </dependency> 
  9. <dependency> 
  10.     <groupId>org.springframework.boot</groupId> 
  11.     <artifactId>spring-boot-starter-web</artifactId> 
  12. </dependency> 
  13. <dependency> 
  14.     <groupId>org.springframework.session</groupId> 
  15.     <artifactId>spring-session-data-redis</artifactId> 
  16. </dependency> 

2.2 配置

  1. spring.redis.password=123 
  2. spring.redis.port=6379 
  3. spring.redis.host=127.0.0.1 
  4.  
  5. spring.security.user.name=javaboy 
  6. spring.security.user.password=123 
  7.  
  8. server.port=8080 

配置一下 Redis 的基本信息;Spring Security 為了簡化,我就將用戶名密碼直接配置在 application.properties 中了,最后再配置一下項目端口號。

2.3 使用

配置完成后 ,就可以使用 Spring Session 了,其實就是使用普通的 HttpSession ,其他的 Session 同步到 Redis 等操作,框架已經自動幫你完成了:

  1. @RestController 
  2. public class HelloController { 
  3.     @Value("${server.port}"
  4.     Integer port; 
  5.     @GetMapping("/set"
  6.     public String set(HttpSession session) { 
  7.         session.setAttribute("user""javaboy"); 
  8.         return String.valueOf(port); 
  9.     } 
  10.     @GetMapping("/get"
  11.     public String get(HttpSession session) { 
  12.         return session.getAttribute("user") + ":" + port; 
  13.     } 

考慮到一會 Spring Boot 將以集群的方式啟動 ,為了獲取每一個請求到底是哪一個 Spring Boot 提供的服務,需要在每次請求時返回當前服務的端口號,因此這里我注入了 server.port 。

接下來 ,項目打包:

 

打包之后,啟動項目的兩個實例:

  1. java -jar session-4-0.0.1-SNAPSHOT.jar --server.port=8080 
  2. java -jar session-4-0.0.1-SNAPSHOT.jar --server.port=8081 

然后先訪問 localhost:8080/set 向 8080 這個服務的 Session 中保存一個變量,第一次訪問時會自動跳轉到登錄頁面,輸入用戶名密碼進行登錄即可。訪問成功后,數據就已經自動同步到 Redis 中 了 :

 

然后,再調用 localhost:8081/get 接口,就可以獲取到 8080 服務的 session 中的數據:

 

此時關于 session 共享的配置就已經全部完成了,session 共享的效果我們已經看到了。

2.4 Security 配置

Session 共享已經實現了,但是我們發現新的問題,在Spring Boot + Vue 前后端分離項目,如何踢掉已登錄用戶?一文中我們配置的 session 并發管理失效了。

也就是說,如果我添加了如下配置:

  1. protected void configure(HttpSecurity http) throws Exception { 
  2.     http.authorizeRequests().anyRequest() 
  3.             ... 
  4.             .sessionManagement() 
  5.             .maximumSessions(1) 
  6.             .maxSessionsPreventsLogin(true); 

現在這個配置不起作用,用戶依然可以在多個瀏覽器上同時登錄。

這是怎么回事呢?

在該文中,我們提到,會話注冊表的維護默認是由 SessionRegistryImpl 來維護的,而 SessionRegistryImpl 的維護就是基于內存的維護。現在我們雖然啟用了 Spring Session+Redis 做 Session 共享,但是 SessionRegistryImpl 依然是基于內存來維護的,所以我們要修改 SessionRegistryImpl 的實現邏輯。

修改方式也很簡單,實際上 Spring Session 為我們提供了對應的實現類 SpringSessionBackedSessionRegistry,具體配置如下:

  1. @Configuration 
  2. public class SecurityConfig extends WebSecurityConfigurerAdapter { 
  3.     @Autowired 
  4.     FindByIndexNameSessionRepository sessionRepository; 
  5.     @Override 
  6.     protected void configure(HttpSecurity http) throws Exception { 
  7.         http.authorizeRequests().anyRequest() 
  8.                 ... 
  9.                 .sessionManagement() 
  10.                 .maximumSessions(1) 
  11.                 .maxSessionsPreventsLogin(true
  12.                 .sessionRegistry(sessionRegistry()); 
  13.     } 
  14.     @Bean 
  15.     SpringSessionBackedSessionRegistry sessionRegistry() { 
  16.         return new SpringSessionBackedSessionRegistry(sessionRepository); 
  17.     } 

我們在這里只需要提供一個 SpringSessionBackedSessionRegistry 的實例,并且將其配置到 sessionManagement 中去即可。以后,session 并發數據的維護將由 SpringSessionBackedSessionRegistry 來完成,而不是 SessionRegistryImpl,如此,我們關于 session 并發的配置就生效了,在集群環境下,用戶也只可以在一臺設備上登錄。

為了讓我們的案例看起更完美一些,接下來我們來引入 Nginx ,實現服務實例自動切換。

3.引入 Nginx

很簡單,進入 Nginx 的安裝目錄的 conf 目錄下(默認是在/usr/local/nginx/conf),編輯 nginx.conf 文件:

 

在這段配置中:

  1. upstream 表示配置上游服務器
  2. javaboy.org 表示服務器集群的名字,這個可以隨意取名字
  3. upstream 里邊配置的是一個個的單獨服務
  4. weight 表示服務的權重,意味者將有多少比例的請求從 Nginx 上轉發到該服務上
  5. location 中的 proxy_pass 表示請求轉發的地址,/ 表示攔截到所有的請求,轉發轉發到剛剛配置好的服務集群中
  6. proxy_redirect 表示設置當發生重定向請求時,nginx 自動修正響應頭數據(默認是 Tomcat 返回重定向,此時重定向的地址是 Tomcat 的地址,我們需要將之修改使之成為 Nginx 的地址)。

配置完成后,將本地的 Spring Boot 打包好的 jar 上傳到 Linux ,然后在 Linux 上分別啟動兩個 Spring Boot 實例:

  1. nohup java -jar session-4-0.0.1-SNAPSHOT.jar --server.port=8080 & 
  2. nohup java -jar session-4-0.0.1-SNAPSHOT.jar --server.port=8081 & 

其中

  • nohup 表示當終端關閉時,Spring Boot 不要停止運行
  • & 表示讓 Spring Boot 在后臺啟動

配置完成后,重啟 Nginx:

  1. /usr/local/nginx/sbin/nginx -s reload 

Nginx 啟動成功后,我們首先手動清除 Redis 上的數據,然后訪問192.168.66.128/set 表示向 session 中保存數據,這個請求首先會到達 Nginx 上,再由 Nginx 轉發給某一個 Spring Boot 實例:

 

如上,表示端口為 8081 的 Spring Boot 處理了這個 /set 請求,再訪問 /get 請求:

 

可以看到,/get 請求是被端口為 8080 的服務所處理的。

4.總結

本文主要向大家介紹了 Spring Session 的使用,另外也涉及到一些 Nginx 的使用 ,雖然本文較長,但是實際上 Spring Session 的配置沒啥,涉及到的配置也都很簡單。

如果大家沒有在 SSM 架構中用過 Spring Session ,可能不太好理解我們在 Spring Boot 中使用 Spring Session 有多么方便,因為在 SSM 架構中,Spring Session 的使用要配置三個地方 ,一個是 web.xml 配置代理過濾器,然后在 Spring 容器中配置 Redis,最后再配置 Spring Session,步驟還是有些繁瑣的,而 Spring Boot 中直接幫我們省去了這些繁瑣的步驟!

好了 ,本文就說到這里,本文相關案例我已經上傳到 GitHub ,大家可以自行下載:https://github.com/lenve/spring-security-samples

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2021-05-31 10:47:17

SpringSecuritySession

2010-05-17 10:04:45

2022-08-08 13:45:12

Redis面試Hash

2023-09-19 22:41:30

控制器HTTP

2011-05-07 09:32:31

Tomcat

2019-08-15 10:20:19

云計算技術安全

2025-02-21 15:43:29

slotredis集群

2019-12-23 10:20:12

Web圖片優化前端

2017-10-26 08:43:18

JavaScript內存處理

2021-03-01 07:31:53

消息支付高可用

2021-05-28 10:40:08

Redis數據庫集群化

2012-12-12 09:49:41

2020-12-29 09:11:33

LinuxLinux內核

2017-03-13 13:21:34

Git處理大倉庫

2021-03-24 10:40:26

Python垃圾語言

2011-02-28 14:08:31

網速變慢局域網網速

2025-01-09 10:20:53

2023-01-04 10:01:21

ReactTypeScript元素

2024-04-16 13:32:57

2011-11-08 09:37:34

負載均衡Linux集群
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品视频在线观看免费 | av在线播放网 | www.日本国产 | 国产一区二区激情视频 | 国产麻豆一区二区三区 | 国产精品久久久久久久7777 | 亚洲免费在线视频 | 国产aaaaav久久久一区二区 | 久久久精品一区二区 | 国产美女精品视频免费观看 | 91美女视频| 免费在线观看一区二区 | 日韩一区二区三区四区五区 | 色综合天天综合网国产成人网 | 国产情侣激情 | 色婷婷久久久久swag精品 | 欧美日韩高清 | 精品国产99 | 国产一区二区麻豆 | 久草久| 国产日韩欧美中文 | 一区二区三区四区国产 | av天天干| 欧美福利 | 日韩精品一区二区三区第95 | 草逼网站 | 99久久婷婷国产亚洲终合精品 | 欧美一区二区三区四区五区无卡码 | 成人在线精品视频 | 亚洲精品乱码久久久久久按摩观 | 午夜国产 | 中文字幕一区二区三区四区五区 | 人人九九精 | 午夜影院在线观看视频 | 国产激情91久久精品导航 | 国内自拍真实伦在线观看 | 在线播放中文字幕 | 欧美一区二区在线播放 | 国产午夜高清 | 亚洲最大成人综合 | 精品久久一区二区三区 |