《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.mzph.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…

刘强东痛批京东高管,拿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…

火狐浏览器书签(收藏夹)全部消失,历史记录也消失,如何恢复

今天关闭再打开火狐浏览器瞬间懵逼&#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;选…

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 ->…

.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…

python拓展7(Celery消息队列配置定时任务)

介绍 celery 定时器是一个调度器&#xff08;scheduler&#xff09;&#xff1b;它会定时地开启&#xff08;kicks off&#xff09;任务&#xff0c;然后由集群中可用的工人&#xff08;worker&#xff09;来执行。 定时任务记录&#xff08;entries&#xff09;默认 从 beat_s…

chrome连接已重置_如何重置(或调整)Chrome的下载设置

chrome连接已重置By default, Chrome saves all downloaded files to the same location—a dedicated “Downloads” folder. The thing is, this isn’t always practical for all types of download files. The good news is you can easily tweak this setting. 默认情况下…

.Net 7 团队把国内的龙芯确实当做一等公民和弃用的项目

楔子&#xff1a;国内龙芯据说是用的自己的指令集&#xff0c;在研究ILC的时候&#xff0c;发现了龙芯在微软那边确实是一等公民的存在。同X64,ARM,X86一同并列交叉编译和二进制提取。龙芯官网龙芯平台.NET&#xff0c;是龙芯公司基于开源社区.NET独立研发适配的龙芯版本&#…

戴尔押宝iSCSI,由低到高组合成型

戴尔&#xff08;Dell&#xff09;是较早接受SAS技术的主流存储厂商之一&#xff0c;2006年已推出采用SAS硬盘驱动器的SAS直连存储&#xff08;DAS&#xff09;系统PowerVault MD3000。一年之后&#xff0c;主机连接改用iSCSI的PowerVault MD3000i问世。2008年1月&#xff0c;E…