網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
前言:
本文使用 .NET Core SDK 3.1 的版本。
1) 關(guān)于Http中的會(huì)話
- Http是一種采用請(qǐng)求響應(yīng)消息交換模式,且無(wú)狀態(tài)的傳輸協(xié)議。
- 該協(xié)議確保客戶端將請(qǐng)求報(bào)文發(fā)送給目標(biāo)服務(wù)器并接收來(lái)自服務(wù)端的響應(yīng)報(bào)文,這個(gè)報(bào)文交換是一個(gè)Http事務(wù)。
- 從協(xié)議的角度講,即使在使用長(zhǎng)連接的情況下,同一個(gè)客戶端和服務(wù)器之間進(jìn)行多個(gè)Http事務(wù)也是完全獨(dú)立的
- 所以需要在應(yīng)用層為兩者去建立一個(gè)上下文來(lái)保存多次消息交換的狀態(tài),這就是所謂的會(huì)話。
2) 關(guān)于 ASP.NET Core 中的會(huì)話
- 在 ASP.NET Core 中利用一個(gè)叫做 Session 的中間件來(lái)實(shí)現(xiàn)會(huì)話,
- 每個(gè)會(huì)話都有一個(gè)標(biāo)識(shí)SessionKey,但是SessionKey不是唯一標(biāo)識(shí),是一個(gè)數(shù)據(jù)字典的形式,
- 將SessionKey保存在服務(wù)端,當(dāng)會(huì)話中間件在處理會(huì)話的第一個(gè)請(qǐng)求的時(shí)候,會(huì)創(chuàng)建一個(gè)SessionKey
- 并基于它創(chuàng)建一個(gè)獨(dú)立的數(shù)據(jù)字典來(lái)存儲(chǔ)會(huì)話狀態(tài),應(yīng)用程序設(shè)置的會(huì)話狀態(tài)都是自動(dòng)保存在當(dāng)前會(huì)話對(duì)應(yīng)的數(shù)據(jù)字典中的
- 這個(gè)SessionKey最終會(huì)以 Cookie 的形式寫入響應(yīng)并返回給客戶端,
- 客戶端在每次發(fā)起請(qǐng)求的時(shí)候都會(huì)附加這個(gè) Cookie,從而使我們的應(yīng)用程序能夠準(zhǔn)確定位到當(dāng)前會(huì)話對(duì)應(yīng)的數(shù)據(jù)字典。
一、配置會(huì)話中間件
配置基于內(nèi)存的分布式緩存服務(wù)和會(huì)話服務(wù),如需要將緩存放置于數(shù)據(jù)庫(kù)可以參考微軟官方文檔
public void ConfigureServices(IServiceCollection services) { // 添加基于內(nèi)存的緩存服務(wù),以供會(huì)話中間件來(lái)使用 collection.AddDistributedMemoryCache(); // 添加會(huì)話 collection.AddSession(); }
添加會(huì)話中間件
public void Configure(IApplicationBuilder app) { // 引入會(huì)話中間件 app.UseSession(); }
二、會(huì)話狀態(tài)的讀寫
寫入Session
ISession session = httpContext.Session; var sessionStartTime = DateTime.Now.ToString(CultureInfo.InvariantCulture); session.SetString("SessionStartTime", sessionStartTime);
讀取Session
ISession session = httpContext.Session; session.TryGetValue("SessionStartTime", out var value); var sessionStartTime = Encoding.UTF8.GetString(value);
獲取SessionId
ISession session = httpContext.Session; session.TryGetValue("SessionStartTime", out var value); var sessionStartTime = Encoding.UTF8.GetString(value);
獲取SessionKey
SessionKey 需要通過(guò)反射獲取
ISession session = httpContext.Session; var field = typeof(DistributedSession).GetTypeInfo() .GetField("_sessionKey", BindingFlags.Instance | BindingFlags.NonPublic); var sessionKey = field?.GetValue(session);
三、 示例的生命周期
這里準(zhǔn)備了示例的代碼:
app.UseEndpoints(endpoints => { endpoints.MapGet("/get", async httpContext => { ISession session = httpContext.Session; string sessionStartTime; if (session.TryGetValue("SessionStartTime", out var value)) { sessionStartTime = Encoding.UTF8.GetString(value); } else { sessionStartTime = DateTime.Now.ToString(CultureInfo.InvariantCulture); session.SetString("SessionStartTime", sessionStartTime); } var field = typeof(DistributedSession).GetTypeInfo() .GetField("_sessionKey", BindingFlags.Instance | BindingFlags.NonPublic); var sessionKey = field?.GetValue(session); var responseText = $@" <html> <body> <h1>Get Session</h1> <ul> <li>Session ID:{session.Id}</li> <li>Session Key:{sessionKey}</li> <li>Session Start Time:{sessionStartTime}</li> <li>Current Time:{DateTime.Now:yyyy-MM-dd HH:mm:ss}</li> </ul> </body> </html>"; httpContext.Response.ContentType = "text/html"; await httpContext.Response.WriteAsync(responseText); }); });
清除瀏覽器中的 Cookie,然后刷新頁(yè)面進(jìn)入/get
頁(yè)面中,可以看到在新的網(wǎng)絡(luò)請(qǐng)求中響應(yīng)標(biāo)頭多了一個(gè) set-cookie,這個(gè)set-cookie是被加密的SessionKey,還具有 httponly 的標(biāo)簽,以防止Cookie 的值被跨站讀取。
默認(rèn)請(qǐng)求下 Cookie 采用的路徑是根路徑
然后我們重新刷新頁(yè)面,可以看到請(qǐng)求標(biāo)頭中多出一個(gè) cookie,就是之前的 set-cookie,因?yàn)橹熬彺姹磺宄院螅谝淮嗡⑿聵?biāo)頭多一個(gè) set-cookie,相當(dāng)于創(chuàng)建一個(gè)新的會(huì)話,當(dāng)下一次發(fā)起請(qǐng)求就會(huì)帶上 cookie。
四、其他
- SessionId 可以作為會(huì)話的唯一標(biāo)識(shí),但是 SessionKey 不可以
- 也就是說(shuō)兩個(gè)不同的 Session,肯定具有不同的 SessionId,但是他們有可能共享相同的SessionKey
- 當(dāng)會(huì)話中間件接收到會(huì)話的第一個(gè)請(qǐng)求的時(shí)候,他會(huì)創(chuàng)建兩個(gè)不同的 guid,分別表示 SessionKey 和 SessionId
- 其中 SessionId 將被作為會(huì)話狀態(tài)的一部分被存儲(chǔ)起來(lái),而 SessionKey 則會(huì)以會(huì)話的形式返回給客戶端
- 會(huì)話一般都是有有效期的,而會(huì)話的有效期基本決定了存儲(chǔ)的會(huì)話狀態(tài)數(shù)據(jù)的有效期
- 默認(rèn)情況下 ASP.NET Core 應(yīng)用的會(huì)話它所采用的默認(rèn)過(guò)期時(shí)間是20分鐘, 默認(rèn)情況下20分鐘內(nèi)的任意請(qǐng)求都會(huì)將會(huì)話的壽命延長(zhǎng)再延長(zhǎng)
- 兩次請(qǐng)求的時(shí)間超過(guò)了有效期,意味著這個(gè)會(huì)話過(guò)期,存儲(chǔ)的會(huì)話狀態(tài)數(shù)據(jù)包括 SessionId 也都會(huì)被清除
- 但是請(qǐng)求攜帶的 SessionKey 可能還是原來(lái)的 SessionKey
- 在這種請(qǐng)求下,會(huì)話中間件會(huì)創(chuàng)建一個(gè)新的會(huì)話,這個(gè)新的會(huì)話具有不同的 SessionId,但是整個(gè)會(huì)話狀態(tài)仍然會(huì)沿用原來(lái)的 SessionKey
- 所以 SessionKey 不能作為會(huì)話的唯一標(biāo)識(shí),它只代表存儲(chǔ)數(shù)據(jù)的標(biāo)識(shí)
- 會(huì)話本質(zhì)上就是在應(yīng)用的層面上提供了一個(gè)數(shù)據(jù)容器來(lái)保存客戶端的狀態(tài),這個(gè)客戶端狀態(tài)就是會(huì)話狀態(tài),會(huì)話的核心功能就是會(huì)話狀態(tài)的讀寫
原文鏈接:https://blog.csdn.net/Upgrader/article/details/114271949
相關(guān)推薦
- 2021-12-16 el-tree 設(shè)置選項(xiàng)框選中狀態(tài),通過(guò)setCheckedKeys設(shè)置,會(huì)導(dǎo)致父選項(xiàng)框選中,子選項(xiàng)
- 2022-06-02 Nginx虛擬主機(jī)的配置步驟過(guò)程全解_nginx
- 2022-06-17 Ruby信號(hào)處理詳解_ruby專題
- 2024-03-06 Springboot實(shí)現(xiàn)緩存預(yù)熱
- 2023-01-23 oracle數(shù)據(jù)排序后獲取前幾行數(shù)據(jù)的寫法(rownum、fetch方式)_oracle
- 2023-01-07 Flutter?Widget開(kāi)發(fā)之Focus組件圖文詳解_Android
- 2022-08-14 Qt控件點(diǎn)擊消息獲取的方法詳解_C 語(yǔ)言
- 2023-03-04 React深入分析更新的創(chuàng)建源碼_React
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支