• ASP.NET Core 2 学习笔记(十一)Cookies & Session


    基本上HTTP是没有记录状态的协定,但可以通过Cookies将Request来源区分出来,并将部分数据暂存于Cookies及Session,是写网站常用的用户数据暂存方式。
    本篇将介绍如何在ASP.NET Core使用Cookie及Session。

    Cookies

    Cookies是将用户数据存在Client的浏览器,每次Request都会把Cookies送到Server。
    在ASP.NET Core中要使用Cookie,可以通过HttpContext.RequestHttpContext.Response存取:

    Startup.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MyWebsite
    {
        public class Startup
        {
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                // app.Run(async (context) =>
                // {
                //     await context.Response.WriteAsync("Hello World!");
                // });
    
                app.Run(async (context) =>
                {
                    string message;
    
                    if (!context.Request.Cookies.TryGetValue("Sample", out message))
                    {
                        message = "Save data to cookies.";
                    }
                    context.Response.Cookies.Append("Sample", "This is Cookies.");
                    // 刪除 Cookies 数据
                    //context.Response.Cookies.Delete("Sample");
    
                    await context.Response.WriteAsync($"{message}");
                });
            }
        }
    }
    

    从HTTP 可以看到传送跟收到的Cookies 信息:

    当存在Cookies 的信息越多,封包就会越大,因为每个Request 都会带着Cookies 数据。

    Session

    Session是通过Cookies内的唯一识别信息,把用户数据存在Server端内存、NoSQL或数据库等。
    要在ASP.NET Core使用Session需要先加入两个服务:

    • Session容器
      Session可以存在不同的地方,透过DI IDistributedCache物件,让Session服务知道要将Session存在哪边。
      (之后的文章会介绍到IDistributedCache分散式快取)
    • Session服务
      在DI容器加入Session服务。并将Session的Middleware加入Pipeline。

    Startup.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    
    namespace MyWebsite
    {
        public class Startup
        {
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
                // 将 Session 存在 ASP.NET Core 内存中
                services.AddDistributedMemoryCache();
                services.AddSession();
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IHostingEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                // SessionMiddleware 加入 Pipeline
                app.UseSession();
    
                app.Run(async (context) =>
                {
                    context.Session.SetString("Sample", "This is Session.");
                    string message = context.Session.GetString("Sample");
                    await context.Response.WriteAsync($"{message}");
                });
            }
        }
    }
    

    HTTP Cookies 信息如下:

    可以看到多出了.AspNetCore.Session.AspNetCore.Session就是Session的唯一识别信息。
    每次Request时都会带上这个值,当Session服务取得这个值后,就会去Session容器找出专属这个值的Session数据。

    对象类型

    以前ASP.NET可以将对象直接存放到Session,现在ASP.NET Core Session不再自动序列化对象到Sesson。
    如果要存放对象到Session就要自己序列化了,这边以JSON格式作为范例:

    ExtensionsSessionExtensions.cs

    using Microsoft.AspNetCore.Http;
    using Newtonsoft.Json;
    
    namespace MyWebsite.Extensions
    {
        public static class SessionExtensions
        {
            public static void SetObject<T>(this ISession session, string key, T value)
            {
                session.SetString(key, JsonConvert.SerializeObject(value));
            }
    
            public static T GetObject<T>(this ISession session, string key)
            {
                var value = session.GetString(key);
                return value == null ? default(T) : JsonConvert.DeserializeObject<T>(value);
            }
        }
    }
    

    通过上面扩展方法,就可以将对象存取至Session,如下:

    using MyWebsite.Extensions;
    using MyWebsite.Models;
    // ...
    var user = context.Session.GetObject<UserModel>("user");
    context.Session.SetObject("user", user);
    

    安全性

    虽然Session数据都存在Server端看似安全,但如果封包被拦截,只要拿到.AspNetCore.Session就可以取到该用户数据,也是有风险。
    有些安全调整建议实作:

    • SecurePolicy
      限制只有在HTTPS连线的情况下,才允许使用Session。如此一来变成加密连线,就不容易被拦截。
    • IdleTimeout
      修改合理的Session到期时间。预设是20分钟没有跟Server互动的Request,就会将Session变成过期状态。
      (20分钟有点长,不过还是要看产品需求。)
    • Name
      没必要将Server或网站技术的信息爆露在外面,所以预设Session名称.AspNetCore.Session可以改掉。
    // ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDistributedMemoryCache();
        services.AddSession(options =>
        {
            options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            options.Cookie.Name = "mywebsite";
            options.IdleTimeout = TimeSpan.FromMinutes(5);
        });
    }
    

    强类型

    由于Cookies及Session预设都是使用字串的方式存取资料,弱类型无法在开发阶段判断有没有打错字,还是建议包装成强类型比较好。
    而且直接存取Cookies/Session的话逻辑相依性太强,对单元测试很不友善,所以还是建议包装一下。

    WappersSessionWapper.cs

    using Microsoft.AspNetCore.Http;
    using MyWebsite.Extensions;
    using MyWebsite.Models;
    // ...
    namespace MyWebsite.Wappers
    {
        public interface ISessionWapper
        {
            UserModel User { get; set; }
        }
    
        public class SessionWapper : ISessionWapper
        {
            private static readonly string _userKey = "session.user";
            private readonly IHttpContextAccessor _httpContextAccessor;
    
            public SessionWapper(IHttpContextAccessor httpContextAccessor)
            {
                _httpContextAccessor = httpContextAccessor;
            }
    
            private ISession Session
            {
                get
                {
                    return _httpContextAccessor.HttpContext.Session;
                }
            }
    
            public UserModel User
            {
                get
                {
                    return Session.GetObject<UserModel>(_userKey);
                }
                set
                {
                    Session.SetObject(_userKey, value);
                }
            }
        }
    }
    

    在DI容器中加入IHttpContextAccessorISessionWapper,如下:

    Startup.cs

    // ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<ISessionWapper, SessionWapper>();
    }
    • IHttpContextAccessor
      ASP.NET Core实现了IHttpContextAccessor,让HttpContext可以轻松的注入给需要用到的对象使用。
      由于IHttpContextAccessor只是取用HttpContext实例的接口,用Singleton的方式就可以供其它物件使用。

    在Controller就可以直接注入ISessionWapper,以强类型的方式存取Session,如下:

    Controllers/HomeController.cs

    using Microsoft.AspNetCore.Mvc;
    using MyWebsite.Wappers;
    
    namespace MyWebsite.Controllers
    {
        public class HomeController : Controller
        {
            private readonly ISessionWapper _sessionWapper;
    
            public HomeController(ISessionWapper sessionWapper)
            {
                _sessionWapper = sessionWapper;
            }
    
            public IActionResult Index()
            {
                var user = _sessionWapper.User;
                if (user == null) user = new Models.UserModel();
                _sessionWapper.User = user;
                return Ok(user);
            }
        }
    }

    参考

    Introduction to session and application state in ASP.NET Core

    老司机发车啦:https://github.com/SnailDev/SnailDev.NETCore2Learning

  • 相关阅读:
    C# 分布式自增ID算法snowflake(雪花算法)
    C# DateTime日期格式化
    HTTP响应状态码参考
    VS2019已还原ReSharper的功能
    2019前端UI框架排行榜
    2019年ASP.NET Core学习路线
    jumpserver堡垒机安装
    Linux shell 时间操作(取昨天 前天)
    nfs共享文件搭建
    zabbix agent配置详解(windows)
  • 原文地址:https://www.cnblogs.com/snaildev/p/9138858.html
Copyright © 2020-2023  润新知