ASP.NET CORE--会话

136 阅读5分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

  1. 概述 HTTP 是一种采用请求/响应消息交换模式且无状态的传输协议。HTTP 协议旨在确保客户端将请求报文发送给目标服务器,并成功接收来自服务端的响应报文,这个基本的报文交换被称为一个HTTP事务(Transaction)。

因此同一个客户端和服务端进行的多个http事务是完全相互独立的,所以需要在应用层为二者建立一个上下文来保存多次消息交换的状态,我们将其称为会话(Session)。

    会话的目的就是在同一个客户端和服务器之间建立两者交谈的语境或者上下文(Context),ASP.NET Core利用一个名为 SessionMiddleware的中间件实现了会话。

2. 设置、提取以及查看会话状态 每个会话都有一个被称为 Session Key 的标识(但不是唯一标识,唯一标识是Session ID),会话状态以一个数据字典的形式将 Session Key 保存在服务端。

    虽然整个会话状态数据存储在服务端,但是用来提取对应会话状态数据的Session Key需要以 Cookie 的形式由客户端来提供。如果请求没有以 Cookie 的形式携带 Session Key,当SessionMiddleware 中间件接收到会话的第一个请求时,它会创建两个不同的 GUID 来分别表示Session Key和 Session ID。其中,Session ID将作为会话状态的一部分被存储起来,而 Session Key以Cookie的形式返回客户端。

    Session Key的值不仅是被加密的,更具有一个httponly标签,以防止Cookie值被跨站读取。

    ASP.NET Core 应用的会话采用的默认过期时间为 20 分。在默认情况下,20分之内的任意一次请求都会将会话的寿命延长至 20 分后。

2.1 示例 ASP.NET Core 应用在默认情况下会利用缓存来存储会话状态,并且默认采用的是分布式缓存。

using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Hosting; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using System; using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder;

namespace App { public class Program { public static void Main() { //调用 IServiceCollection 接口的 AddDistributedRedisCache 扩展方法添加了基于DistributedRedisCache 的服务注册。 //SessionMiddleware 中间件的注册则通过调用该 IApplication Builder接口的UseSession扩展方法来完成 Host.CreateDefaultBuilder() .ConfigureWebHostDefaults(builder => builder .ConfigureServices(svcs => svcs .AddDistributedRedisCache(options => options.Configuration = "localhost") .AddSession()) .Configure(app => app .UseSession() .Run(ProcessAsync))) .Build() .Run(); //从当前HttpContext上下文中获取表示会话的 Session对象,并调用其 TryGetValue方法获取会话开始时间 static async Task ProcessAsync(HttpContext context) { var session = context.Session; await session.LoadAsync(); string sessionStartTime; if (session.TryGetValue("SessionStartTime", out var value)) { //由于 TryGetValue方法总是以字节数组的形式返回会话状态值,所以我们采用 UTF-8 编码转换成字符串形式。 sessionStartTime = Encoding.UTF8.GetString(value); } else { //调用SetString方法采用相同的Key进行设置 sessionStartTime = DateTime.Now.ToString(); session.SetString("SessionStartTime", sessionStartTime); }

            context.Response.ContentType = "text/html";
            await context.Response.WriteAsync($"<html><body><ul><li>Session ID:{session.Id}</li>");
            await context.Response.WriteAsync($"<li>Session Start Time:{sessionStartTime}</li>");
            await context.Response.WriteAsync($"<li>Current Time:{DateTime.Now}</li></ul></table></body></html>");
        }
    }
}

}

  1. 会话状态的读写 3.1 ISession接口 在应用编程接口层面,ASP.NET Core应用的会话通过如下所示的 ISession接口来表示。

1)会话状态的所有操作(设置、提取、移除和清除)都是通过调用 ISession 接口相应的方法(Set、TryGetValue、Remove和 Clear)都是在内存中进行的

2)设置和提取的缓存状态的值体现为字节数组,所以应用程序需要自行完成序列化和反序列化的工作。

3)Keys属性得到所有会话状态条目的 Key

4)ISession对象在会话状态尚未全部加载到内存之前处于不可用的状态,其 IsAvailable属性返回 False。会话状态一旦被加载,IsAvailable属性马上变成 True。

5)在操作缓存状态之前调用 ISession 对象的 LoadAsync 方法以异步的方式将所有的会话状态加载到内存中,这是一种推荐的做法

6)由于作用于 ISession 对象上的 4 个基本会话状态操作都是针对内存的,这些操作最终需要通过 CommitAsync方法做统一的提交。SessionMiddleware中间件会在完成请求处理之前调用这个方法,该方法会将当前请求针对会话状态的改动保存到后备存储中。另外,只有在当前请求上下文中真正对会话状态做了相应改动的情况下,ISession 对象的 CommitAsync 方法才会真正执行提交操作。

3.2 DistributedSession ASP.NET Core 应用在默认情况下会采用分布式缓存来存储会话状态,如下所示的DistributedSession类型就是针对ISession接口的默认实现。

3.3 ISessionStore ISessionStore接口可以视为创建表示会话的ISession对象的工厂。

    应用程序利用ISession接口表示的服务来读写会话状态,ISessionStore接口表示创建ISession对象的工厂。DistributedSessionStore是对ISessionStore的默认实现,它创建的是一个 DistributedSession 对象,后者利用 IDistributedCache 对象表示的分布式缓存来存储会话状态。

会话模型核心接口与类型之间的关系

  1. SessionMiddleware中间件 在请求处理过程中,利用注册的 ISessionStore 对象来创建表示会话的ISession对象是由 SessionMiddleware中间件来完成的。

4.1 SessionOptions 由于保存会话状态的Session Key是通过Cookie进行传递的,所以SessionOptions承载的核心配置选项是 Cookie属性表示的 CookieBuilder对象。

4.2 ISessionFeature HttpContext上下文的 Session 属性来源于一个通过 ISessionFeature 接口表示的特性。

4.3 SessionMiddleware