关于 ASP.NET Core 中的会话中间件

前言:

本文使用 .NET Core SDK 3.1 的版本。

1) 关于Http中的会话

Http是一种采用请求响应消息交换模式,且无状态的传输协议。
该协议确保客户端将请求报文发送给目标服务器并接收来自服务端的响应报文,这个报文交换是一个Http事务。
从协议的角度讲,即使在使用长连接的情况下,同一个客户端和服务器之间进行多个Http事务也是完全独立的,
所以需要在应用层为两者去建立一个上下文来保存多次消息交换的状态,这就是所谓的会话。

2) 关于 ASP.NET Core 中的会话

在 ASP.NET Core 中利用一个叫做 Session 的中间件来实现会话,
每个会话都有一个标识SessionKey,但是SessionKey不是唯一标识,是一个数据字典的形式,

将SessionKey保存在服务端,当会话中间件在处理会话的第一个请求的时候,会创建一个SessionKey
并基于它创建一个独立的数据字典来存储会话状态,应用程序设置的会话状态都是自动保存在当前会话对应的数据字典中的,
这个SessionKey最终会以 Cookie 的形式写入响应并返回给客户端,
客户端在每次发起请求的时候都会附加这个 Cookie,从而使我们的应用程序能够准确定位到当前会话对应的数据字典。

一、配置会话中间件

配置基于内存的分布式缓存服务和会话服务,如需要将缓存放置于数据库可以参考微软官方文档

	public void ConfigureServices(IServiceCollection services)
	{
		// 添加基于内存的缓存服务,以供会话中间件来使用
        collection.AddDistributedMemoryCache();
        // 添加会话
        collection.AddSession();
	}

添加会话中间件

	public void Configure(IApplicationBuilder app)
	{
		// 引入会话中间件
		app.UseSession();
	}

二、会话状态的读写

写入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;
    var sessionId = session.Id;

获取SessionKey
SessionKey 需要通过反射获取

    ISession session = httpContext.Session;
    var field = typeof(DistributedSession).GetTypeInfo()
        .GetField("_sessionKey", BindingFlags.Instance | BindingFlags.NonPublic);
    var sessionKey = field?.GetValue(session);

三、 示例的生命周期

这里准备了示例的代码:

    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,然后刷新页面进入/get页面中
可以看到在新的网络请求中响应标头多了一个 set-cookie,这个set-cookie是被加密的SessionKey,还具有 httponly 的标签,以防止 Cookie 的值被跨站读取。
默认请求下 Cookie 采用的路径是根路径
然后我们重新刷新页面,可以看到请求标头中多出一个 cookie,就是之前的 set-cookie
因为之前缓存被清除以后,第一次刷新标头多一个 set-cookie,相当于创建一个新的会话,当下一次发起请求就会带上 cookie。

四、其他

SessionId 可以作为会话的唯一标识,但是 SessionKey 不可以,
也就是说两个不同的 Session,肯定具有不同的 SessionId,但是他们有可能共享相同的 SessionKey,
当会话中间件接收到会话的第一个请求的时候,他会创建两个不同的 guid,分别表示 SessionKey 和 SessionId,
其中 SessionId 将被作为会话状态的一部分被存储起来,而 SessionKey 则会以会话的形式返回给客户端,
会话一般都是有有效期的,而会话的有效期基本决定了存储的会话状态数据的有效期,
默认情况下 ASP.NET Core 应用的会话它所采用的默认过期时间是20分钟, 默认情况下20分钟内的任意请求都会将会话的寿命延长再延长,
两次请求的时间超过了有效期,意味着这个会话过期,存储的会话状态数据包括 SessionId 也都会被清除,
但是请求携带的 SessionKey 可能还是原来的 SessionKey,
在这种请求下,会话中间件会创建一个新的会话,这个新的会话具有不同的 SessionId,但是整个会话状态仍然会沿用原来的 SessionKey,
所以 SessionKey 不能作为会话的唯一标识,它只代表存储数据的标识。
会话本质上就是在应用的层面上提供了一个数据容器来保存客户端的状态,这个客户端状态就是会话状态,会话的核心功能就是会话状态的读写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值