Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (二)

目录

  • Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (二)

    • Cookie加密
    • Cookie伪加密(序列化/反序列化)
    • 减少Cookie中Value的大小
    • 自定义Cookie管理功能
    • 总结
    • 未完待续......

Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (二)

? 上篇文章我们介绍了Microsoft.AspNetCore.Authentication.Cookies的部分内容,由于篇幅问题,不得不分多篇进行介绍,本篇我们继续之前的介绍。

Demo源码地址

Cookie加密

? 在之前的Demo中,浏览器Cookie中的.AspNetCore.Cookies是一个加密字符串,默认Asp.Net Core是对该值做过加密处理的,至于加密方式我们不得而知(翻看源码是可以找到一些蛛丝马迹的), 如果我们要修改加密方式只有一种办法就是通过DataProtectionProvider属性设置我们自己的实现:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
            //设置我们自己的数据加密实现
            options.DataProtectionProvider = new MyDataProtectionProvider();
        })
        .AddCookie("Admin","Admin",options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromDays(1);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
        });
    ......
}

MyDataProtectionProvider

public class MyDataProtector : IDataProtector
{
    private string _prupose;

    public MyDataProtector() : this(null)
    { }

    public MyDataProtector(string purpose)
    {
        _prupose = purpose;
    }

    public IDataProtector CreateProtector(string purpose)
    {
        _prupose = purpose;
        //我们也可以在这里返回多个实例
        //return new MyDataProtector(purpose);
        return this;
    }

    public byte[] Protect(byte[] plaintext)
    {
        return plaintext
    }

    public byte[] Unprotect(byte[] protectedData)
    {
        return protectedData;
    }
}

MyDataProtector是我们自定义的加密类,这里我们没有实现ProtectUnprotect方法,只是简单的做了一个return操作,具体要使用什么加密方式,还是交给需要他的人吧,这里我们只演示一下如何自定义加密类就行了。

Cookie伪加密(序列化/反序列化)

? 伪加密说的是TicketDataFormat属性,上文我们介绍了IDataProtector接口中定义的加密解密方法,这里我们介绍ISecureDataFormat接口中也定义的ProtectUnprotect方法,从字面两上看两个接口的方法意思一样,但是从实现代码看,完全不是一回事,ISecureDataFormat接口处理的并不是加密,准确的说它的作用是序列化与反序列化,我们可以看一下Asp.Net Coro中的默认实现SecureDataFormat

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
            options.DataProtectionProvider = new MyDataProtector();
            //使用我们自己的序列化实现,这里的代码看起来有点别扭,上一行我们已经设置MyDataProtector实例,这里又设置一次,原因是Asp.Net Core框架本身在这里设计的有点问题,如果通过依赖注入的方式就可以避免这种让人费解的写法。
            options.TicketDataFormat =new MyTicketDataFormat(new MyDataProtector());
        })
        .AddCookie("Admin","Admin",options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromDays(1);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
        });
    ......
}

MyTicketDataFormat

public class MyTicketDataFormat : TicketDataFormat
{
    public MyTicketDataFormat(IDataProtector dataProtector):base(dataProtector)
    {}
    public new string Protect(AuthenticationTicket data)
    {
        return base.Protect(data);
    }

    public new string Protect(AuthenticationTicket data, string purpose)
    {
        return base.Protect(data, purpose);
    }

    public new AuthenticationTicket Unprotect(string protectedText)
    {
        return base.Unprotect(protectedText);
    }

    public new AuthenticationTicket Unprotect(string protectedText, string purpose)
    {
        return base.Unprotect(protectedText, purpose);
    }
}

我们定义了一个序列化类MyTicketDataFormat,这里我们继承的父类是TicketDataFormatTicketDataFormat是Asp.Net Core的默认实现,如果我们没有设置options.TicketDataFormat,默认使用的序列化类就是TicketDataFormat,如果在真实的项目中你需要自定义序列化时,你应该实现ISecureDataFormat<AuthenticationTicket>接口而不是像我这样偷懒,我这里纯粹是为了演示,我们看一下Asp.Net Core是如何实现SecureDataFormat类的(SecureDataFormat是TicketDataFormat的父类)。这里有一点需要注意,如果你实现了ISecureDataFormat<AuthenticationTicket>接口别忘了在构造函数中注入IDataProtector,如果你真的忘记了,那么你上文设置的options.DataProtectionProvider = new MyDataProtectionProvider();将不会起任何作用(我认为这里是一个大坑)

减少Cookie中Value的大小

? 默认情况下Asp.Net Core的实现是将AuthenticationTicket对象序列化并加密存储在Cookie中,那么这会有一个隐患,随着AuthenticationTicket对象存储数据越来越多,Cookie也会越来越大,但是浏览器只给了我们4K的存储空间,有可能在某一天你的Cookie就装不下你的数据流,所以减少Cookie的存储是我们或许终将考虑的一个问题;从另一个方面看,减少Cookie的数据量也利于网络的传输。

? 那么我们怎么减小Cookie的存储空间呢?!我们不妨借鉴一下Asp.Net Core中Session的实现原理,将Cookie信息,写入内存、缓存服务器(redis)中,然后通过唯一标识与它对应,这样便可以极大的减少Cookie的存储传输体积。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
            options.DataProtectionProvider = new MyDataProtectionProvider();
            options.TicketDataFormat =new MyTicketDataFormat(new MyDataProtector());
            //添加Cookie存储实现
            options.SessionStore = new MyTicketStore();
        })
        .AddCookie("Admin","Admin",options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromDays(1);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
        });
    ......
}

MyTicketStore

public class MyTicketStore : ITicketStore
{
    private Dictionary<string, AuthenticationTicket> _cache = new Dictionary<string, AuthenticationTicket>();

    public Task RemoveAsync(string key)
    {
        _cache.Remove(key);
        return Task.FromResult(0);
    }

    public Task RenewAsync(string key, AuthenticationTicket ticket)
    {
        _cache[key]= ticket;
        return Task.FromResult(0);
    }

    public Task<AuthenticationTicket> RetrieveAsync(string key)
    {
        _cache.TryGetValue(key, out AuthenticationTicket ticket);
        return Task.FromResult(ticket);
    }

    public Task<string> StoreAsync(AuthenticationTicket ticket)
    {
        var key = Guid.NewGuid().ToString("n");
        _cache.TryAdd(key, ticket);
        return Task.FromResult(key);
    }
}

上面的代码我们通过实现ITicketStore的自定义类型MyTicketStore来自定义AuthenticationTicket数据的存储,在MyTicketStore中我们通过Dictionary<string, AuthenticationTicket>来存储认证数据,这里其实应该使用内存,或者外部缓存系统,例如:用Redis来存储认证数据。

自定义Cookie管理功能

? 我们这里说的Cookie管理指的是ICookieManager接口,该接口主要是用来添加,删除,获取Cookie的信息,也就是Microsoft.AspNetCore.Authentication.Cookies真正将Cookie写入http头,从http头获取Cookie的入口。

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    ......
    services.AddAuthentication()
        .AddCookie(options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
            options.DataProtectionProvider = new MyDataProtectionProvider();
            options.TicketDataFormat =new MyTicketDataFormat(new MyDataProtector());
            options.SessionStore = new MyTicketStore();
            options.CookieManager = new MyCookieManager();
        })
        .AddCookie("Admin","Admin",options=>{
            options.SlidingExpiration = true;
            options.ExpireTimeSpan = TimeSpan.FromDays(1);
            options.Cookie.Expiration = TimeSpan.FromDays(365);
        });
    ......
}

MyCookieManager

public class MyCookieManager : ICookieManager
{
    public void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options)
    {
        context.Response.Cookies.Append(key, value, options);
    }

    public void DeleteCookie(HttpContext context, string key, CookieOptions options)
    {
        context.Response.Cookies.Delete(key, options);
    }

    public string GetRequestCookie(HttpContext context, string key)
    {
        return context.Request.Cookies[key];
    }
}

我们这里的实现很简单,仅仅调用了一下HttpContext上的对象来操作Cookie,在实现上可能会存在一些问题,更安全的实现请看ChunkingCookieManager

总结

  1. 我们介绍了如何通过IDataProtector接口实现自己的加密方式 。
  2. 我们介绍了如何通过ISecureDataFormat<AuthenticationTicket>接口实现自己的序列化方式,并提到了这里可能有坑,当我们自定义序列化方式的时候,需要再构造函数中添加IDataProtector参数,不然可能导致DataProtectionProvider属性失去意义。
  3. 我们介绍如何通过ITicketStore接口实现类似Session的存储机制,来减少Cookie在浏览器中的存储以及传输量。
  4. 我们介绍了如何通过ICookieManager接口实现Cookie的自定义管理功能。

未完待续......

原文地址:https://www.cnblogs.com/guodf/p/9716262.html

时间: 2024-10-08 22:21:23

Microsoft.AspNetCore.Authentication.Cookies从入门到精通 (二)的相关文章

iOS开发-UI 从入门到精通(二)

iOS开发-UI 从入门到精通(二)是对 iOS开发-UI 从入门到精通(一)知识点的巩固,主要以习题练习为主,增强实战经验,为以后做开发打下坚实的基础! ※开发环境和注意事项: 1.前期iOS-UI开发我们需要手动管理内存,所以我们要把ARC关掉(Xcode关掉ARC的步骤): (1)打开Xcode选中当前工程: (2)选中Build Settings: (3)在输入框内输入count: (4)选择Objective-C Automatic Reference Counting  将其设置为 

Python基本语法,python入门到精通[二]

在上一篇博客Windows搭建python开发环境,python入门到精通[一]我们已经在自己的windows电脑上搭建好了python的开发环境,这篇博客呢我就开始学习一下Python的基本语法.现在练习的开发环境是基于windows的,如果有朋友喜欢linux或者mac的话,只要有需求,我后续的可以给出对应的博文介绍,其实也是换汤不换药,没需求我就懒得说了. v写在前面 python你不去认识它,可能没什么,一旦你认识了它,你就会爱上它 v郑重承诺 我承认,现在园子里烂尾的系列博文比比皆是,

mybatis从入门到精通(二) 增删查改

mybatis从入门到精通(二) 增删查改 一丶前言 "增删查改"是后台开发的常用操作, 因此, 学习mybatis或者其他orm框架有必要统一学习一下"增删查改". 二丶准备开发环境 使用"mybatis从入门到精通(一) 入门"准备的开发环境 三丶查 -- <select/> UserMapper.java User selectUser(@Param("userId") int userId); UserMa

C语言指针教程----入门到精通&lt;二&gt;

数组与指针: 一: 指针既然可以指向变量,那么必然可以指向一个数组.本节探讨数组与指针的关系.首先读者必须明白,数组名代表数组中首元素的地址. 那么如何表示数组中其他元素的地址以及其他元素本身呢? p+i或a+i就是a[i]的地址:相应的, *(p+i)或*(a+i)就表示的a[i]元素. 已经提到了数组名代表数组首元素地址了,那么所有用数组名做参数的函数都可以改写 成用指针做参数的函数. 二:多维数组的情况. 这里,读者首先有个意识,多维数组每一维也都是一维数组.假设有个二维数组a[3][4]

Atom编辑器入门到精通(二) 插件的安装和管理

在本节中我们会学习如果安装和使用插件插件是Atom中一个非常重要的组成部分,很多功能都是以插件形式存在的.比如上篇文章中提到的目录树和设置等窗口都是通过默认安装的插件来实现的. 查看已安装的插件 打开设置窗口(Cmd+,),再切换到"Packages"标签页,就可以看到已安装的插件列表了我们可以发现Atom默认安装了70多个插件,Atom通过这些插件提供了各种非常有用的特性和功能另外在设置窗口的"Themes"标签页中也能看到所有安装过的主题(主题其实也是一种插件)

MyBatis 入门到精通(二) SQL语句映射XML文件

MyBatis 真正强大之处就在这些映射语句,也就是它的魔力所在.对于它的强大功能,SQL 映射文件的配置却非常简单. 如果您比较SQL 映射文件配置与JDBC 代码,您很快可以发现,使用SQL 映射文件配置可以节省95%的代码量.MyBatis 被创建来专注于SQL,但又给您自己的实现极大的空间. 需要配置的基本元素 1. cache – 配置给定模式的缓存 2. cache-ref – 从别的模式中引用一个缓存 3. resultMap – 这是最复杂而却强大的一个元素了,它描述如何从结果集

flutter从入门到精通二

静态方法和静态属性(static): 通过static修饰的方法和属性称为静态方法和静态属性,注意静态方法和静态属性只能通过类名访问,不能通过对象访问. 静态方法不能访问非静态的属性和非静态方法,反正可以 class Person{ String name; int age; Person(this.name,this.age); printInfo(){ //在字符串中,可以直接通过${}访问类的属性和方法 print('${this.name}---${this.age}'); } } cl

Python基本语法[二],python入门到精通[四]

在上一篇博客Python基本语法,python入门到精通[二]已经为大家简单介绍了一下python的基本语法,上一篇博客的基本语法只是一个预览版的,目的是让大家对python的基本语法有个大概的了解.之所以将python的基本语法分为两部分,是因为园子里大部分人都是有编程基础的,所以在学习Python的时候可以先出个预览版的,预览版都是根据Python的语法与其他语言(java/C#/php)比较类似又不完全一样的地方,便于大家对Python有个基本理解.在大家差不多有个了解以后,我们再深入讲讲

Microsoft.AspNetCore.Localization 自定义多语言获取

1 自定义 多语言加载 ,加载中间件 public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.C