SpringSession原理簡析

本文借鑒于:Spring-Session 原理簡析 - 知乎 (zhihu.com)

目錄

概述

使用方式

原理

總結


概述

Session的原理

Session是存在服務器的一種用來存放用戶數據的類哈希表結構,當瀏覽器第一次發送請求的時候服務器會生成一個hashtable和一個sessionid,sessionid來唯一標識這個hashtable,響應的時候會通過一個響應頭set-cookie返回給瀏覽器,瀏覽器再將這個sessionid存儲在一個名為JESSIONID的cookie中。 接著瀏覽器在發送第二次請求時,就會帶上這個cookie,這個cookie會存儲sessionid,并發送給服務器,服務器根據這個sessionid找到對應的用戶信息。

分布式下Session共享的問題

如果在不同域名環境下需要進行session共享,比如在auth.gulimall.com登錄成功之后要將登陸成功的用戶信息共享給gulimall.com服務,由于域名不同,普通的session就會不起作用。并且如果是同一個服務,復制多份,session也不會共享。

SpringSession

SpringSession 是一個 Spring 項目中用于管理和跟蹤會話的框架。它提供了一種抽象層,使得會話數據可以存儲在不同的后端數據結構中(例如內存、數據庫、Redis 等),并且支持跨多個請求的會話管理。

Spring Session 的核心功能包括:

(1)跨多個請求共享會話數據:SpringSession 使用一個唯一的會話標識符來跟蹤用戶的會話,并且可以在不同的請求中共享會話數據,無論是在同一個應用程序的不同服務器節點之間,還是在不同的應用程序之間。

(2)多種后端存儲支持:SpringSession 支持多種后端存儲,包括內存、數據庫和緩存系統(如 Redis)。你可以根據應用程序的需求選擇合適的存儲方式。

(3)會話過期管理:SpringSession 提供了會話過期管理的功能,可以根據一定的策略自動清理過期的會話數據。 會話事件監聽:Spring Session 允許開發人員注冊會話事件監聽器,以便在會話創建、銷毀或屬性更改時執行一些自定義邏輯。

(4)使用 SpringSession 可以使得在分布式環境中管理會話變得更加簡單和靈活,同時也提供了更多的擴展性和可定制性。

SpringSession 可以在多個微服務之間共享 session 數據。

使用方式

添加依賴

<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId>
</dependency>

添加注解@EnableRedisHttpSession

@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class RedisSessionConfig {}

maxInactiveIntervalInSeconds: 設置 Session 失效時間,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 屬性不再生效。也可以在yml文件上配置

spring:  session:store-type: redistimeout: 30m #這個過期時間是在瀏覽器關閉之后才開始計時

經過上面的配置后,Session 調用就會自動去Redis存取。另外,想要達到 Session 共享的目的,只需要在其他的系統上做同樣的配置即可。

原理

看了上面的配置,我們知道開啟 Redis Session 的“秘密”在 @EnableRedisHttpSession 這個注解上。打開 @EnableRedisHttpSession 的源碼(shift+shift):

/*** 用于啟用基于Redis的HTTP會話管理的配置注解。* 該注解會將RedisHttpSessionConfiguration配置類導入到Spring配置中,* 從而支持將會話信息存儲在Redis中。** @author <NAME>* @see RedisHttpSessionConfiguration*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({RedisHttpSessionConfiguration.class})
@Configuration(proxyBeanMethods = false
)
public @interface EnableRedisHttpSession {/*** 會話的最大不活動間隔時間(秒)。默認值為1800秒(30分鐘)。* 該屬性定義了會話在Redis中存儲的有效期。* * @return 最大不活動間隔時間(秒)*/int maxInactiveIntervalInSeconds() default 1800;/*** Redis中用于存儲會話數據的命名空間。默認值為"spring:session"。* 通過設置不同的命名空間,可以實現多個應用會話的隔離。* * @return Redis存儲會話數據的命名空間*/String redisNamespace() default "spring:session";/*** 已棄用的Redis刷新模式設置。建議使用{@link #flushMode()}替代。* * @return Redis刷新模式,默認為ON_SAVE* @deprecated 使用{@link #flushMode()}替代*/@DeprecatedRedisFlushMode redisFlushMode() default RedisFlushMode.ON_SAVE;/*** 定義Redis中數據的刷新模式。可配置為在每次保存屬性時刷新(ON_SAVE),* 或者在請求結束時刷新(ON_COMMIT)。* * @return 刷新模式,默認為ON_SAVE*/FlushMode flushMode() default FlushMode.ON_SAVE;/*** 定義清理過期會話的CRON表達式。默認值為"0 * * * * *",即每分鐘執行一次。* 通過調整該表達式,可以控制清理過期會話的頻率。* * @return 清理過期會話的CRON表達式*/String cleanupCron() default "0 * * * * *";/*** 定義會話屬性保存的模式。可配置為僅在設置屬性時保存(ON_SET_ATTRIBUTE),* 或者在每次請求結束時保存(ALWAYS)。* * @return 會話屬性保存模式,默認為ON_SET_ATTRIBUTE*/SaveMode saveMode() default SaveMode.ON_SET_ATTRIBUTE;
}

這個注解的主要作用是注冊一個 SessionRepositoryFilter,這個 Filter 會攔截所有的請求,對 Session 進行操作。

SessionRepositoryFilter 攔截到請求后,會先將 request 和 response 對象轉換成 Spring 內部的包裝類 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 對象。SessionRepositoryRequestWrapper 類重寫了原生的getSession方法。代碼如下:

@Override
public HttpSessionWrapper getSession(boolean create) {//通過request的getAttribue方法查找CURRENT_SESSION屬性,有直接返回HttpSessionWrapper currentSession = getCurrentSession();if (currentSession != null) {return currentSession;}//查找客戶端中一個叫SESSION的cookie,通過sessionRepository對象根據SESSIONID去Redis中查找SessionS requestedSession = getRequestedSession();if (requestedSession != null) {if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {requestedSession.setLastAccessedTime(Instant.now());this.requestedSessionIdValid = true;currentSession = new HttpSessionWrapper(requestedSession, getServletContext());currentSession.setNew(false);//將Session設置到request屬性中setCurrentSession(currentSession);//返回Sessionreturn currentSession;}}else {// This is an invalid session id. No need to ask again if// request.getSession is invoked for the duration of this requestif (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");}setAttribute(INVALID_SESSION_ID_ATTR, "true");}//不創建Session就直接返回nullif (!create) {return null;}if (SESSION_LOGGER.isDebugEnabled()) {SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for "+ SESSION_LOGGER_NAME,new RuntimeException("For debugging purposes only (not an error)"));}//通過sessionRepository創建RedisSession這個對象,可以看下這個類的源代碼,如果//@EnableRedisHttpSession這個注解中的redisFlushMode模式配置為IMMEDIATE模式,會立即//將創建的RedisSession同步到Redis中去。默認是不會立即同步的。S session = SessionRepositoryFilter.this.sessionRepository.createSession();session.setLastAccessedTime(Instant.now());currentSession = new HttpSessionWrapper(session, getServletContext());setCurrentSession(currentSession);return currentSession;
}

當調用 SessionRepositoryRequestWrapper 對象的getSession方法拿 Session 的時候,會先從當前請求的屬性中查找CURRENT_SESSION屬性,如果能拿到直接返回,這樣操作能減少Redis操作,提升性能。

到現在為止我們發現如果redisFlushMode配置為 ON_SAVE 模式的話,Session 信息還沒被保存到 Redis 中,那么這個同步操作到底是在哪里執行的呢?

仔細看代碼,我們發現 SessionRepositoryFilter 的doFilterInternal方法最后有一個 finally 代碼塊,這個代碼塊的功能就是將 Session同步到 Redis。

@Override
protected void doFilterInternal(HttpServletRequest request,HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response, this.servletContext);SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);try {filterChain.doFilter(wrappedRequest, wrappedResponse);}finally {//將Session同步到Redis,同時這個方法還會將當前的SESSIONID寫到cookie中去,同時還會發布一//SESSION創建事件到隊列里面去wrappedRequest.commitSession();}
}

總結

主要的核心類有:

  • EnableRedisHttpSession:開啟 Session 共享功能;
  • RedisHttpSessionConfiguration:配置類,一般不需要我們自己配置,主要功能是配置 SessionRepositoryFilter 和 RedisOperationsSessionRepository 這兩個Bean;
  • SessionRepositoryFilter:攔截器,Spring-Session 框架的核心;
  • RedisOperationsSessionRepository:可以認為是一個 Redis 操作的客戶端,有在 Redis 中進行增刪改查 Session 的功能;
  • SessionRepositoryRequestWrapper:Request 的包裝類,主要是重寫了getSession方法
  • SessionRepositoryResponseWrapper:Response的包裝類。

原理簡要總結:

當請求進來的時候,SessionRepositoryFilter 會先攔截到請求,將 request 和 response 對象轉換成 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 。后續當第一次調用 request 的getSession方法時,會調用到 SessionRepositoryRequestWrapper 的getSession方法。這個方法是被重寫過的,邏輯是先從 request 的屬性中查找,如果找不到,再查找一個key值是"SESSION"的 Cookie,通過這個 Cookie 拿到 SessionId 去 Redis 中查找,如果查不到,就直接創建一個RedisSession 對象,同步到 Redis 中。

說的簡單點就是:攔截請求,將之前在服務器內存中進行 Session 創建、銷毀的動作,改成在 Redis 中進行。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/14448.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/14448.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/14448.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

論文閱讀--Language-driven Semantic Segmentation

效果很好&#xff0c;文本增加一個詞&#xff0c;就能找到對應的分割地方&#xff0c;給出的無用標簽也不會去錯誤分割&#xff0c;而且能理解文本意思&#xff0c;例如dog和pet都能把狗給分割出來 image encoder使用DPT分割模型&#xff0c;大致架構為ViTdecoder&#xff0c;d…

【個人經歷分享】末流本科地信,畢業轉碼經驗

本人24屆末流本科&#xff0c;地理信息科學專業。 我們這個專業可以說是 “高不成&#xff0c;低不就”的專業&#xff0c;什么都學但都不精。考研我實在是卷不動同學歷的人&#xff0c;我在大三的時候就開始考慮轉碼。 至于我為什么選擇轉碼&#xff0c;選擇了GIS開發&#xf…

element ui 下拉框Select 選擇器 上下箭頭旋轉方向樣式錯亂——>優化方案

目錄 前言1、問題復現2、預期效果3、input框樣式修改解析4、修改方案 &#x1f680;寫在最后 前言 測試A&#xff1a;那啥&#xff01;摳圖仔&#xff0c;樣式怎么點著點著就出問題了。 前端&#xff1a;啥&#xff1f;css樣式錯亂了&#xff1f;你是不是有緩存啊&#xff01…

js常用數組方法

1.arr.push() -末尾添加 該方法可以向數組末尾添加一個或多個元素&#xff0c;并返回數組新的長度可以將要添加的元素作為方法的參數傳遞&#xff0c;這樣這些元素將會自動添加到元素的末尾原數組會發生變化 var arr [ 1, 2, 3, 4 ] arr.push(5) console.log(arr) // [ 1, …

linux命令arp的使用

arp arp 命令用于顯示和修改 IP 到 MAC 轉換表 補充說明 arp 命令 是 Address Resolution Protocol&#xff0c;地址解析協議&#xff0c;是通過解析網絡層地址來找尋數據鏈路層地址的一個網絡協議包中極其重要的網絡傳輸協議。而該命令可以顯示和修改 arp 協議解析表中的緩…

Mia for Gmail for Mac:Mac用戶的郵件管理首選

對于追求高效工作的Mac用戶來說&#xff0c;Mia for Gmail for Mac無疑是郵件管理的首選工具。它以其卓越的性能和豐富的功能&#xff0c;為用戶帶來了前所未有的高效郵件管理體驗。 Mia for Gmail for Mac不僅支持多帳號登錄和標簽選擇功能&#xff0c;還提供了郵件分類、垃圾…

linux 中 fd 申請和釋放管理(兩級 bitmap)

linux 中 fd 的幾點理解_linux fd-CSDN博客 通過上邊的文章&#xff0c;我們可以知道&#xff0c;在 linux 中&#xff0c;fd 有以下幾點需要了解&#xff1a; &#xff08;1&#xff09;fd 表示進程打開的文件&#xff0c;是進程級別的資源&#xff0c;不是系統級別的資源 …

【前端每日一題】day11

一個盒子(DIV)里有若干個小盒子&#xff0c;每個小盒子里還可能有多個小盒子 多層盒子結構。每個盒子都有一個唯一的id和 name 屬性。現在給出一個盒子的 id 請找到這個盒子并打開&#xff0c;輸出這個盒子內部所有小盒子的id和 name&#xff0c;并繼續打開這些小盒子輸出id和 …

【Unity】Unity項目轉抖音小游戲(四)一些常用方法

1.初始化 SDK會在Unity啟動前就初始化好&#xff0c;但是又有Init的接口&#xff0c;所以這里通過 StarkSDK.s_ContainerEnv 判斷有沒有初始化&#xff0c;沒有的話就手動初始化 public override void Init(string code, Action callback){Debug.Log("初始化抖音SDK"…

AIGC全面介紹

AIGC&#xff08;Artificial Intelligence Generated Content&#xff09;&#xff0c;即生成式人工智能&#xff0c;是人工智能1.0時代進入2.0時代的重要標志。這一技術的出現&#xff0c;標志著人工智能從計算智能、感知智能邁向了認知智能的新階段。以下是關于AIGC的全面介紹…

基于manifest文件批量將coding的倉庫導入gitlab中

文章目錄 寫在前面的話背景編寫manifest文件最終效果 寫在前面的話 前面有講過通過manifest清單導入項目到gitlab中&#xff0c;但是實際的操作是不同gitlab實例之間的操作&#xff0c;然而對于在不同gitlab實例的repo遷移而言&#xff0c;顯然direct transfer會更合適。 背景…

民國漫畫雜志《時代漫畫》第21期.PDF

時代漫畫21.PDF: https://url03.ctfile.com/f/1779803-1248634754-017e2b?p9586 (訪問密碼: 9586) 《時代漫畫》的雜志在1934年誕生了&#xff0c;截止1937年6月戰爭來臨被迫停刊共發行了39期。 ps: 資源來源網絡!

代碼隨想錄算法訓練營Day49 | 123.買賣股票的最佳時機III、188.買賣股票的最佳時機IV | Python | 個人記錄向

本文目錄 123.買賣股票的最佳時機III做題看文章 188.買賣股票的最佳時機IV做題 以往忽略的知識點小結個人體會 123.買賣股票的最佳時機III 代碼隨想錄&#xff1a;123.買賣股票的最佳時機III Leetcode&#xff1a;123.買賣股票的最佳時機III 做題 無思路。 看文章 確定dp數…

結構型模式之橋接模式

文章目錄 概述原理結構圖代碼示例 小結 概述 橋接模式(bridge pattern) 的定義是&#xff1a;將抽象部分與它的實現部分分離&#xff0c;使它們都可以獨立地變化。 橋接模式用一種巧妙的方式處理多層繼承存在的問題,用抽象關聯來取代傳統的多層繼承,將類之間的靜態繼承關系轉…

使用位掩碼的權限設計

使用位掩碼的權限設計 權限系統的設計幾乎是每個系統都必需的模塊。 下面就聊一聊基本設計的思路。 位掩碼&#xff08;BitMask&#xff09;&#xff0c;是位&#xff08;Bit&#xff09;和掩碼&#xff08;Mask&#xff09;的組合詞。 “位”指代著二進制數據當中的二進制位…

基于深度學習OCR文本識別系統源碼(帶界面)

第一步&#xff1a;概要 基于深度學習OCR文本識別分為兩個模塊&#xff1a;DBNet和CRNN。 DBNet是基于分割的文本檢測算法&#xff0c;算法將可微分二值化模塊(Differentiable Binarization)引入了分割模型&#xff0c;使得模型能夠通過自適應的閾值圖進行二值化&#xff0c;并…

Postgresql 基礎學習

一、介紹 PostgreSQL是一個開源的關系型數據庫管理系統&#xff08;RDBMS&#xff09;&#xff0c;它支持SQL語言的所有功能&#xff0c;具有可擴展性、高并發性和可靠性等特點。 以下是一些 PostgreSQL 的特點&#xff1a; 開源&#xff1a;PostgreSQL是一個非常受歡迎的開源…

Python-溫故知新

1快速打開.ipynb文件 安裝好anaconda后&#xff0c;在需要打開notebook的文件夾中&#xff0c; shift鍵右鍵——打開powershell窗口——輸入jupyter notebook 即可在該文件夾中打開notebook的頁面&#xff1a; 2 快速查看函數用法 光標放在函數上——shift鍵tab 3...

Docker鏡像源自動測試鏡像速度,并選擇速度最快的鏡像

國內執行如下代碼 bash <(curl -sSL https://gitee.com/xjxjin/scripts/raw/main/check_docker_registry.sh)國外執行如下代碼 bash <(curl -sSL https://github.com/xjxjin/scripts/raw/main/check_docker_registry.sh)如果有老鐵有比較不錯的鏡像源&#xff0c;可以提…

探索Python編程樂趣:制作氣泡反彈小游戲

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、引言&#xff1a;Python編程的輕松入門 二、游戲實現原理&#xff1a;氣泡反彈的邏輯 …