《ASP.NET Core 6框架揭秘實例》演示[35]:利用Session保留語境

客戶端和服務器基于HTTP的消息交換就好比兩個完全沒有記憶能力的人在交流,每次單一的HTTP事務體現為一次“一問一答”的對話。單一的對話毫無意義,在在同一語境下針對某個主題進行的多次對話才會有結果。會話的目的就是在同一個客戶端和服務器之間建立兩者交談的語境或者上下文,ASP.NET Core利用一個名為SessionMiddleware的中間件實現了會話。本篇提供了幾個簡單的實例來演示如何在一個ASP.NET Core應用中利用會話來存儲用戶的狀態。[本文節選《ASP.NET Core 6框架揭秘》第23章]。

[S2301]設置和提取會話狀態(源代碼)
[S2302]查看存儲的會話狀態(源代碼)
[S2303] 查看Cookie(源代碼)

[S2301]設置和提取會話狀態

每個會話都有一個被稱為Session Key的標識(但不是唯一標識),會話狀態以一個數據字典的形式將Session Key保存在服務端。當SessionMiddleware中間件在處理會話的第一個請求時,它會創建一個Session Key,并據此創建一個獨立的數據字典來存儲會話狀態。這個Session Key最終以Cookie的形式寫入響應并返回客戶端,客戶端在每次發送請求時會自動附加這個Cookie,那么應用程序能夠準確識別會話并成功定位存儲會話狀態的數據字典。

下面我們利用一個簡單的實例來演示會話狀態的讀寫。ASP.NET應用在默認情況下會利用分布式緩存來存儲會話狀態。我們采用基于Redis數據庫的分布式緩存,所以需要添加針對NuGet包“Microsoft.Extensions.Caching.Redis”的依賴。下面的演示程序調用了AddDistributedRedisCache擴展方法添加了基于DistributedRedisCache的服務注冊,SessionMiddleware中間件則通過調用UseSession擴展方法進行注冊。

using?System.Text;var?builder?=?WebApplication.CreateBuilder();
builder.Services.AddDistributedRedisCache(options?=>?options.Configuration?=?"localhost").AddSession();
var?app?=?builder.Build();
app.UseSession();
app.MapGet("/{foobar?}",?ProcessAsync);
app.Run();static?async?ValueTask<IResult>?ProcessAsync(HttpContext?context)
{var?session?=?context.Session;await?session.LoadAsync();string?sessionStartTime;if?(session.TryGetValue("__SessionStartTime",?out?var?value)){sessionStartTime?=?Encoding.UTF8.GetString(value);}else{sessionStartTime?=?DateTime.Now.ToString();session.SetString("__SessionStartTime",?sessionStartTime);}var?html?=?$@"
<html><head><title>Session?Demo</title></head><body><ul><li>Session?ID:{session.Id}</li><li>Session?Start?Time:{sessionStartTime}</li><li>Current?Time:{DateTime.Now}</li><ul></body>
</html>";return?Results.Content(html,?"text/html");
}

我們針對路由模板“/{foobar?}”注冊了一個終結點,后者的處理器指向ProcessAsync方法。該方法當前HttpContext上下文中獲取表示會話的Session對象,并調用其TryGetValue方法獲取會話開始時間,這里使用的Key為“__SessionStartTime”。由于TryGetValue方法總是以字節數組的形式返回會話狀態值,所以我們采用UTF-8編碼轉換成字符串形式。如果會話開始時間尚未設置,我們會調用SetString方法采用相同的Key進行設置。我們最終生成一段用于呈現Session ID和當前實時時間HTML,并封裝成返回的ContentResult對象。程序啟動之后,我們利用Chrome和IE訪問請求注冊的終結點,從圖1可以看出針對Chrome的兩次請求的Session ID和會話狀態值都是一致的,但是IE中顯示的則不同。

18848dc62d297d95d1f170048cf31f18.png
圖1 以會話狀態保存的“會話開始時間”

[S2302]查看存儲的會話狀態

會話狀態在默認情況下采用分布式緩存的形式來存儲,而我們的實例采用的是基于Redis數據庫的分布式緩存,那么會話狀態會以什么樣的形式存儲在Redis數據庫中的呢?由于緩存數據在Redis數據庫中是以散列的形式存儲的,所以我們只有知道具體的Key才能知道存儲的值。緩存狀態是基于作為會話標識的Session Key進行存儲的,它與Session ID具有不同的值,到目前為止我們不能使用公布出來的API來獲取它,但可以利用反射的方式來獲取Session Key。在默認情況下,表示Session的是一個DistributedSession對象,它通過如下所示的字段_sessionKey表示這個用來存儲會話狀態的Session Key。

public?class?DistributedSession?:?ISession
{private?readonly?string?_sessionKey;...
}

接下來我們對上面演示的程序做簡單的修改,從而使Session Key能夠呈現出來。如下面的代碼片段所示,我們可以采用反射的方式得到代表當前會話的DistributedSession對象的_sessionKey字段的值,并將它寫入響應HTML文檔的主體內容中。

static?async?ValueTask<IResult>?ProcessAsync(HttpContext?context)
{var?session?=?context.Session;await?session.LoadAsync();string?sessionStartTime;if?(session.TryGetValue("__SessionStartTime",?out?var?value)){sessionStartTime?=?Encoding.UTF8.GetString(value);}else{sessionStartTime?=?DateTime.Now.ToString();session.SetString("__SessionStartTime",?sessionStartTime);}var?field?=?typeof(DistributedSession).GetTypeInfo().GetField("_sessionKey",?BindingFlags.Instance?|?BindingFlags.NonPublic)!;var?sessionKey?=?field.GetValue(session);var?html?=?$@"
<html><head><title>Session?Demo</title></head><body><ul><li>Session?ID:{session.Id}</li><li>Session?Start?Time:{sessionStartTime}</li><li>Session?Key:{sessionKey}</li><li>Current?Time:{DateTime.Now}</li><ul></body>
</html>";return?Results.Content(html,?"text/html");
}

按照同樣的方式啟動應用后,我們使用瀏覽器訪問目標站點得到的輸出結果如圖2所示,可以看到,Session Key的值被正常呈現出來,它是一個不同于Session ID的GUID。

0a1a8f19c2af6d5345c6cfcba054f77e.png
圖2 呈現當前會話的Session Key

如果有這個保存當前會話狀態的Session Key,我們就可以按照圖3所示的方式采用命令行的形式將存儲在Redis數據庫中的會話狀態數據提取出來。當會話狀態在采用默認的分布式緩存進行存儲時,整個數據字典(包括Key和Value)會采用預定義的格式序列化成字節數組,這基本上可以從圖3體現出來。我們還可以看出基于會話狀態的緩存默認采用的是基于滑動時間的過期策略,默認采用的滑動過期時間為20分(12 000 000 000納秒)。

6d6b76c399d3cc972d1746761fd9060a.png
圖3 存儲在Redis數據庫中的會話狀態

[S2303]?查看Cookie

雖然整個會話狀態數據存儲在服務端,但是用來提取對應會話狀態數據的Session Key需要以Cookie的形式由客戶端來提供。如果請求沒有以Cookie的形式攜帶Session Key,SessionMiddleware中間件就會將當前請求視為會話的第一次請求。在此情況下,它會生成一個GUID作為Session Key,并最終以Cookie的形式返回客戶端。

HTTP/1.1?200?OK
...
Set-Cookie:.AspNetCore.Session=CfDJ8CYspSbYdOtFvhKqo9CYj2vdlf66AUAO2h2BDQ9%2FKoC2XILfJE2bk
IayyjXnXpNxMzMtWTceawO3eTWLV8KKQ5xZfsYNVlIf%2Fa175vwnCWFDeA5hKRyloWEpPPerphndTb8UJNv5R68bGM8jP%2BjKVU7za2wgnEStgyV0ceN%2FryfW;?path=/;?httponly

如上所示的代碼片段是響應報頭中攜帶Session Key的Set-Cookie報頭在默認情況下的表現形式。可以看出Session Key的值不僅是被加密的,更具有一個httponly標簽以防止Cookie值被跨站讀取。在默認情況下,Cookie采用的路徑為“/”。當我們使用同一個瀏覽器訪問目標站點時,發送的請求將以如下形式附加上這個Cookie。

GET?http://localhost:5000/?HTTP/1.1
...
Cookie:?.AspNetCore.Session=CfDJ8CYspSbYdOtFvhKqo9CYj2vdlf66AUAO2h2BDQ9%2FKoC2XILfJE2bkIayyjXnXpNxMzMtWTceawO3eTWLV8KKQ5xZfsYNVlIf%2Fa175vwnCWFDeA5hKRyloWEpPPerphndTb8UJNv5R68bGM8jP%2BjKVU7za2wgnEStgyV0ceN%2FryfW

除了Session Key,前面還提到了Session ID,讀者可能不太了解兩者具有怎樣的區別。Session Key和Session ID是兩個不同的概念,上面演示的實例也證實了它們的值其實是不同的。Session ID可以作為會話的唯一標識,但是Session Key不可以。兩個不同的Session肯定具有不同的Session ID,但是它們可能共享相同的Session Key。當SessionMiddleware中間件接收到會話的第一個請求時,它會創建兩個不同的GUID來分別表示Session Key和Session ID。其中Session ID將作為會話狀態的一部分被存儲起來,而Session Key以Cookie的形式返回客戶端。

會話是具有有效期的,會話的有效期基本決定了存儲的會話狀態數據的有效期,默認過期時間為20分鐘。在默認情況下,20分鐘之內的任意一次請求都會將會話的壽命延長至20分鐘后。如果兩次請求的時間間隔超過20分鐘,會話就會過期,存儲的會話狀態數據(包括Session ID)會被清除,但是請求攜帶可能還是原來的Session Key。在這種情況下,SessionMiddleware中間件會創建一個新的會話,該會話具有不同的Session ID,但是整個會話狀態依然沿用這個Session Key,所以Session Key并不能唯一標識一個會話。

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

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

相關文章

Vincross孫天齊:人機界面的突破將引發科技革命

8月23—27日&#xff0c;世界機器人大會在北京舉辦&#xff0c;全球各國機器人領域的優秀企業悉數亮相&#xff0c;五花八門的機器人及產業鏈上下游最新技術均能在這次盛會上找到蹤跡&#xff0c;整個會場充滿了未來感與時代發展的氣息。 大會中智慧城市服務機器人技術與應用專…

如何在Windows上使用64位Web瀏覽器

Google and Mozilla now offer 64-bit versions of Chrome and Firefox for Windows. Here’s how to find out what version you’re running and how to upgrade. Google和Mozilla現在提供適用于Windows的64位版本的Chrome和Firefox。 這是找出正在運行的版本以及如何升級的方…

立下“去O”Flag的AWS,悄悄修煉了哪些內功?

AWS re:Invent 2018大會上&#xff0c;AWS首席執行執行官Andy Jassy 表示到 2019 年底&#xff0c;亞馬遜將全面放棄使用 Oracle 數據庫&#xff0c;97&#xff05;的“關鍵任務數據庫”將運行在亞馬遜自己的數據庫服務上。 如今&#xff0c;2019年已經過去了四分之一&#xff…

static作用:靜態變量的生存周期和作用域

首先要理解生存周期與作用域的區別&#xff1a; 生存周期: 變量從定義到銷毀的時間范圍。存放在全局數據區的變量的生存周期存在于整個程序運行期間&#xff0c;而存放在棧中的數據則隨著函數等的作用域結束導致出棧而銷毀&#xff0c;除了靜態變量之外的局部變量都存放于棧中…

劉強東痛批京東高管,拿PPT騙他!網友怒了:愛用 PPT 忽悠的人,他們都遭人痛恨...

這是頭哥侃碼的第272篇原創因為被新冠感染&#xff0c;所以最近兩周都在休養。前幾天&#xff0c;我無意中看到一則有關劉強東的新聞&#xff0c;大致是他在京東內部管理培訓會上痛批部分高管&#xff0c;稱 “拿PPT和假大空詞匯忽悠自己的人就是騙子”&#xff0c;表示部分高管…

關于file的部分簡單命令

1.關于file的簡單命令 2.創建/刪除 文件/目錄 ## -f和-r可以連用&#xff0c;表示強制刪除 3.文件/目錄的復制 ##復制是一個新建的過程&#xff0c;在保持原有不變的基礎上重新再建立一個 4.文件/目錄的移動 ##移動是一個重命名的過程&#xff0c;但不改變其中的內容 本文轉自…

字節與浮點型轉換軟件_如何與另一個防病毒軟件一起運行惡意軟件字節

字節與浮點型轉換軟件Malwarebytes Anti-Malware is a great security tool that’s particularly effective against “potentially unwanted programs (PUPs)” and other nasty software traditional antivirus programs don’t deal with. But it’s intended to be used a…

vsftpd服務的搭建

1.vsftpd介紹vsftpd&#xff1a;是非常安全的ftp守護進程(Very secure ftp Daemon)。進程&#xff1a;正在進行&#xff08;運行running&#xff09;的程序。守護進程Daemon&#xff1a;網絡服務類的程序都會有守護進程。守護進程是指實時監測服務訪問狀態的程序。通常都是在系…

火狐瀏覽器書簽(收藏夾)全部消失,歷史記錄也消失,如何恢復

今天關閉再打開火狐瀏覽器瞬間懵逼&#xff0c;瀏覽器所有的記錄都沒了&#xff0c;映入眼簾的的火狐新手指導頁&#xff0c;而且主頁導航變成了hao123&#xff0c;我估計是外部程序篡改了瀏覽器配置&#xff0c;或者其他異常導致瀏覽器重置。書簽、歷史記錄對開發人員的重要性…

apple tv 開發_如何防止Apple TV進入睡眠狀態

apple tv 開發Your Apple TV, by default, goes to sleep fairly quickly when not in use. That’s great for power saving but not so great if you like to keep it on. Let’s take a look at how to extend how long it stays awake or disable sleep mode altogether. 默…

MASA MAUI Plugin (七)應用通知角標(小紅點)Android+iOS

背景MAUI的出現&#xff0c;賦予了廣大Net開發者開發多平臺應用的能力&#xff0c;MAUI 是Xamarin.Forms演變而來&#xff0c;但是相比Xamarin性能更好&#xff0c;可擴展性更強&#xff0c;結構更簡單。但是MAUI對于平臺相關的實現并不完整。所以MASA團隊開展了一個實驗性項目…

SAP如何查看會計憑證

比如SAP中已經存在著很多會計憑證&#xff0c;你想要進入SAP隨便看看會計憑證的列表&#xff0c;怎么操作呢&#xff1f;事務碼 IDCNDOC運行結果看到了憑證們&#xff0c;和每個憑證的行項目們上圖看到的結果比較凌亂實際上我們重新進入IDCNDOC可以通過輸入的勾選&#xff0c;選…

Spring Data Redis與Jedis的選擇(轉)

說明&#xff1a;內容可能有點舊&#xff0c;需要在業務上做權衡。 Redis的客戶端有兩種實現方式&#xff0c;一是可以直接調用Jedis來實現&#xff0c;二是可以使用Spring Data Redis&#xff0c;通過Spring的封裝來調用。應該使用哪一個呢&#xff1f;基于當前版本Spring Dat…

C# 溫故而知新:Stream篇(五)

MemoryStream 目錄&#xff1a; 1 簡單介紹一下MemoryStream 2 MemoryStream和FileStream的區別 3 通過部分源碼深入了解下MemoryStream 4 分析MemorySteam最常見的OutOfMemory異常 5 MemoryStream 的構造 6 MemoryStream 的屬性 7 MemoryStream 的方法 8 MemoryStream 簡單示例…

dosbox 自動運行_如何使用DOSBox運行DOS游戲和舊應用

dosbox 自動運行New versions of Windows don’t fully support classic DOS games and other old applications — this is where DOSBox comes in. It provides a full DOS environment that runs ancient DOS apps on modern operating systems. Windows的新版本不完全支持經…

WPF 自定義放大鏡控件

控件名&#xff1a;Magnifier作 者&#xff1a;WPFDevelopersOrg - 驚鏵原文鏈接[1]&#xff1a;https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用.NET40&#xff1b;Visual Studio 2019;實現此功能需要用到 VisualBrush &#xff0c;放大鏡展現使用 Canvas ->…

springboot小筆記

如果默認通過IDEA的springboot 插件布置的 的初始啟動類是這樣的&#xff0c;這種就是一個普通的java類&#xff0c;只能以jar打包 package com.how2java.springboot;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.Sprin…

.NET實現之(WebBrowser數據采集—續篇)

我們繼續“.NET實現之(WebBrowser數據采集)“系列篇之最后一篇&#xff0c;這篇本人打算主要講解怎么用WebBrowser控件來實現“虛擬”的交互性程序&#xff1b;比如我們用Winform做為宿主容器&#xff0c;用Asp.net做相關收集程序頁面&#xff0c;我們需要通過客戶端填寫相關數…

ipad和iphone切圖_如何在iPhone,iPad和Mac上使消息靜音

ipad和iphone切圖If you use Messages on your iPhone, iPad, or Mac, then you probably know how quickly you can become overrun with message notifications, especially if you’re part of a group message. Thankfully, there’s an easy way to mute specific message…

Pipy 實現 SOCKS 代理

上篇我們介紹了服務網格 osm-edge 出口網關使用的 HTTP 隧道&#xff0c;其處理方式與另一種代理有點類似&#xff0c;就是今天要介紹的 SOCKS 代理。二者的主要差別簡單來說就是前者使用 HTTP CONNECT 告知代理目的地址&#xff0c;而后者則是通過 SOCKS 協議。值得一提的是&a…