Middleware (2) — Session

Feng Gao
5 min readAug 24, 2021

1 Overview

Session 的作用是什么呢?我们都知道 Http 协议是无状态的,也就是说每次 Http 请求都是独立的,它们之间不会知道彼此之间的联系。而 Session 的机制可以让 Http 请求存在相关性。

那么在 Asp.NET Core 应用程序使用 Session 呢?

  • Services.AddDistributedMemoryCache() 表明使用分布式内存作为 Session 存储的位置
  • Service.AddSession() 设置Session 的信息,比如有效时间是 10 秒钟,客户端的 Cookie 信息
  • app.UseSession 开始使用 Session 这个中间件.

当注册完 Session 中间件后, 就可以在 Controller使用 HttpContext.Session 来进行操作。上面的例子中,在访问 https://example.com/ 的时候,设置了 SessionNameAge 两个字段,如果浏览器紧接着访问 https://example.com/about 时候,我们在页面上仍然看到之前的配置。通过这种方式,在一段时间内保存了 Http 请求之间的关联性。

Session 的数据流是这样的

  1. 客户端的浏览器带着 Cookie 来访问服务器,其中 .AspNetCore.Sesion=foobarSession 中间件关系的 Cookie
  2. 根据 foobar 作为 SessionKey 去后端的存储的地方通过 load拿到 Session ,如果没有或者 expired 了,这创建这个 Session 对象,并且将这个 Session 作为 HttpContext 的一个 Feature 以便在 HttpContext 中使用
  3. Session 这个中间件执行完毕,调用 Commit 方法,将处理后的数据,保存到后端的存储中

2. SessionOptions

  • CookieBuilder Cookie 可以 Build 生成CookieOptions , 在这其中有一些默认值
  • IdleTimeout : Session 的有效时间
  • IOTimeOut : Session 在存取的超时时间

3. ISessionStore/DistributedSessionStore

ISessionStoreSession 的存储层,这个接口的 API 的定义如下

ISession Create(string sessionKey, TimeSpan idleTimeout, TimeSpan ioTimeout, Func<bool> tryEstablishSession, bool isNewSessionKey);

这个返回返回一个 ISession 对象,不管这个 Session 是新建的还是已有的。

DistributedSessionStore 是其中的默认实现。 Create 方法回一个 DistributedSession 实现。

DistributedSession

分布式缓存数据保存和读取是由 IDistributedCache 这个接口完成,而内存中以 Dictionary<EncodedKey, byte[]> 形式存在。也即是说, Session 以字段的形式存在,Key 为字符串,Value 则是字节数组,所以可以是任何形式。

ISession 所有的操作都会委托给 Dictionary<EncodedKey, byte[]> 处理。

由于 IDistributedCache 的读取和写入的时候, key 为字符串, value 也是字符串。由于我们 sessionKey 只有一个,所以涉及到下面的

Deserialize: byte[] -> Dictionary<EncodedKey,  byte[]>
Serialize: Dictionary<EncodedKey, byte[]> -> byte[]

它们之间的协议是这样的

  • 版本号: 1 byte
  • 记录数目: 3 bytes
  • SessionId: 16 bytes
  • 对于每个记录
  • key length: 2 bytes
  • key : n bytes
  • value length: 4 bytes
  • value: n bytes

4. SessionMiddleware

SessionMiddlewareInvoke 方法如下

Cookie 中没有包含 Session key 或者是不合法的 Session Key , 需要创建一个新的 Session key

  • 分配一个 16 字节的数组,并且随机填充
  • 然后通过 _dataProtector 来加密存放到 cookie 中的值
  • 然后通过 SessionEstablisher 来注册 Response 开始的时候,往 headercookie 写入数据,比如这里的 Session key

在有了合法的 Session key 之后,

  • 通过 Create 方法创建与一个 ISession 对象,
  • 将它赋值给 HttpFeatures 中的 ISessionFeature
  • 后续的中间件可以通过 HttpContext.Session 来获得创建的 ISession 对象

在后续的中间件处理完毕之后,需要将 Session 执行 CommitAsync 方法,来保存获取或者修改后的 Session 对象。

--

--

Feng Gao

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready