博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
详解Microsoft.AspNetCore.CookiePolicy
阅读量:5864 次
发布时间:2019-06-19

本文共 11512 字,大约阅读时间需要 38 分钟。

目录

详解Asp.Net Core中的Cookie策略

这篇主要介绍这个类库的作用。

功能介绍

  1. 实现IResponseCookies接口,添加、删除cookie时加入自定义控制方法,并支持全局cookie属性设置。
  2. 实现CookieOptions.IsEssential的功能,该属性标识当前属性是否必须的或是否绕过ITrackingConsentFeature的检查。
  3. 实现ITrackingConsentFeature接口,该接口主要是向cookie中添加并检索用户确认设置。

使用Cookie策略

Asp.Net Core是一个高度组件化的框架,很多功能比如授权,认证,回话状态等都是通过中间件的方式引入的,而Microsoft.AspNetCore.CookiePolicy扩展也是通过中间件的方式引入的。

在项目的Startup中添加如下代码:

public class Startup{    public void Configure(IApplicationBuilder app)    {        ...        //cookie策略提供了UseCookiePolicy的两个重载版本        app.UseCookiePolicy();                //app.UseCookiePolicy(new CookiePolicyOptions        //{        //    CheckConsentNeeded = _ => true,        //    HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.None,        //    MinimumSameSitePolicy = SameSiteMode.Strict,        //    Secure = CookieSecurePolicy.SameAsRequest,        //    OnAppendCookie = (context) =>        //    {        //        context.IssueCookie = true;        //    },        //    OnDeleteCookie = (context) =>        //    {        //    }        //});        ...        app.UseMvc();    }}

从UseCookiePolicy方法入手

打开文件,可以看到有两个UseCookiePolicy方法的重载版本,我们先从这个无参的UseCookiePolicy开始介绍,在无参方法中通过UseMiddleware方法添加了一个CookiePolicyMiddleware中间件,如下代码:

//C#扩展类,可以为已有类库添加扩展方法public static class CookiePolicyAppBuilderExtensions{    //为IApplicationBuilder添加扩展方法    public static IApplicationBuilder UseCookiePolicy(this IApplicationBuilder app)    {        ...        //为http管道添加中间件        return app.UseMiddleware
(); } }

下面我们看中间件做了些什么。

通过context.Features.Set方法,分别设置了,的实现类

public class CookiePolicyMiddleware{    public Task Invoke(HttpContext context)    {        var feature = context.Features.Get
() ?? new ResponseCookiesFeature(context.Features); var wrapper = new ResponseCookiesWrapper(context, Options, feature, _logger); //这个类中我们主要看下面这两个Set context.Features.Set
(new CookiesWrapperFeature(wrapper)); //实现gdrp context.Features.Set
(wrapper); return _next(context); } //IResponseCookiesFeature实现 private class CookiesWrapperFeature : IResponseCookiesFeature { public CookiesWrapperFeature(ResponseCookiesWrapper wrapper) { Cookies = wrapper; } public IResponseCookies Cookies { get; } }}

大家可能注意到context.Features这个对象,如果不明白请移步。

通过Set方法将IReponseCookiesFeature的实现设置成了CookiesWrapperFeatureCookiesWrapperFeature有一个参数类型为的构造函数,而这个类实现了两个接口,, ,接下来我们来介绍这两个接口的作用。

实现IResponseCookies接口

通过IReponseCookies接口ResponseCooiesWraper重写了Append、Delete方法,接下来我们看看它是如何实现的:

internal class ResponseCookiesWrapper : IResponseCookies, ITrackingConsentFeature{    private CookiePolicyOptions Options { get; }    public ResponseCookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature, ILogger logger)    {        ...        Options = options;        ...    }    public void Append(string key, string value)    {        //如果我们绑定了OnAppendCookie事件,则每次添加cookie的时候都会触发该事件        if (CheckPolicyRequired() || Options.OnAppendCookie != null)        {            Append(key, value, new CookieOptions());        }        else        {            Cookies.Append(key, value);        }    }    public void Append(string key, string value, CookieOptions options)    {        if (options == null)        {            throw new ArgumentNullException(nameof(options));        }        //使用全局cookie配置修改options        if (ApplyAppendPolicy(ref key, ref value, options))        {            Cookies.Append(key, value, options);        }        else        {            _logger.CookieSuppressed(key);        }    }    //这个方法判断如果cookie不能被跟踪、设置了相同站点要求、设置了cookie只读、设置cookie安全等任何一个选项则采用Cookie策略的实现    private bool CheckPolicyRequired()    {        return !CanTrack            || Options.MinimumSameSitePolicy != SameSiteMode.None            || Options.HttpOnly != HttpOnlyPolicy.None            || Options.Secure != CookieSecurePolicy.None;    }    private bool ApplyAppendPolicy(ref string key, ref string value, CookieOptions options)    {           //如果启用了跟踪或这个cookie是必须的        var issueCookie = CanTrack || options.IsEssential;        //这里去修改options的配置为全局cookies配置        ApplyPolicy(key, options);        //如果我们绑定了添加cookie事件        if (Options.OnAppendCookie != null)        {            var context = new AppendCookieContext(Context, options, key, value)            {                IsConsentNeeded = IsConsentNeeded,                HasConsent = HasConsent,                IssueCookie = issueCookie,            };            //这里执行我们在statup中绑定的方法            Options.OnAppendCookie(context);                key = context.CookieName;            value = context.CookieValue;            //通过设置context.IssueCookie为true可以将cookie写入到浏览器cookie            issueCookie = context.IssueCookie;        }            return issueCookie;    }    //Delete方法的实现与Append方法相同,具体请查看Append方法中的注释    public void Delete(string key)    {        ...    }        public void Delete(string key, CookieOptions options)    {        ...    }    //使用全局cookie配置替换options参数对应的属性    private void ApplyPolicy(string key, CookieOptions options)    {        switch (Options.Secure)        {            case CookieSecurePolicy.Always:                if (!options.Secure)                {                    options.Secure = true;                    _logger.CookieUpgradedToSecure(key);                }                break;            case CookieSecurePolicy.SameAsRequest:                // Never downgrade a cookie                if (Context.Request.IsHttps && !options.Secure)                {                    options.Secure = true;                    _logger.CookieUpgradedToSecure(key);                }                break;            case CookieSecurePolicy.None:                break;            default:                throw new InvalidOperationException();        }        switch (Options.MinimumSameSitePolicy)        {            case SameSiteMode.None:                break;            case SameSiteMode.Lax:                if (options.SameSite == SameSiteMode.None)                {                    options.SameSite = SameSiteMode.Lax;                    _logger.CookieSameSiteUpgraded(key, "lax");                }                break;            case SameSiteMode.Strict:                if (options.SameSite != SameSiteMode.Strict)                {                    options.SameSite = SameSiteMode.Strict;                    _logger.CookieSameSiteUpgraded(key, "strict");                }                break;            default:                throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Options.MinimumSameSitePolicy.ToString()}");        }        switch (Options.HttpOnly)        {            case HttpOnlyPolicy.Always:                if (!options.HttpOnly)                {                    options.HttpOnly = true;                    _logger.CookieUpgradedToHttpOnly(key);                }                break;            case HttpOnlyPolicy.None:                break;            default:                throw new InvalidOperationException($"Unrecognized {nameof(HttpOnlyPolicy)} value {Options.HttpOnly.ToString()}");        }    }}

通过代码,我们可以看出,给CookiePolicyOptions对象的OnAppendCookieOnDeleteCookie的属性设置方法可以实现在每次添加、删除cookie时触发改方法的调用,通常我们可以在这里面做一些全局的过滤,配置什么,最终我们需要设置CookiePolicyOptions.IssueCookie来确认是否要将改cookie发送到浏览器中。

实现ITrackingConsentFeature接口

该接口定义了cookie在什么情况才会被跟踪,并将在浏览器中设置对应的cookie值。

public interface ITrackingConsentFeature{    bool IsConsentNeeded { get; }    bool HasConsent { get; }    bool CanTrack { get; }    void GrantConsent();    void WithdrawConsent();    string CreateConsentCookie();}internal class ResponseCookiesWrapper : IResponseCookies, ITrackingConsentFeature{    //是否需要用户确认显式确认    public bool IsConsentNeeded    {        get        {            if (!_isConsentNeeded.HasValue)            {                _isConsentNeeded = Options.CheckConsentNeeded == null ? false                //从CookiePolicyOptions的CheckConsentNeeded方法获取是否需要用户确认操作                    : Options.CheckConsentNeeded(Context);                _logger.NeedsConsent(_isConsentNeeded.Value);            }            return _isConsentNeeded.Value;        }    }    //判断用户之前是否开启了确认    public bool HasConsent    {        get        {            if (!_hasConsent.HasValue)            {                //从我们之前设置的cookie中获取确认cookie的值                var cookie = Context.Request.Cookies[Options.ConsentCookie.Name];                _hasConsent = string.Equals(cookie, ConsentValue, StringComparison.Ordinal);                _logger.HasConsent(_hasConsent.Value);            }            return _hasConsent.Value;        }    }    //能否跟踪,如果浏览器没有开启用户确认或者浏览器已经确认过了则允许跟踪    public bool CanTrack => !IsConsentNeeded || HasConsent;        //向cookie中写入跟踪启用标识    public void GrantConsent()    {        if (!HasConsent && !Context.Response.HasStarted)        {            var cookieOptions = Options.ConsentCookie.Build(Context);                        Append(Options.ConsentCookie.Name, ConsentValue, cookieOptions);            _logger.ConsentGranted();        }        _hasConsent = true;    }    //撤销之前cookie中写入的跟踪启用标识    public void WithdrawConsent()    {        if (HasConsent && !Context.Response.HasStarted)        {            var cookieOptions = Options.ConsentCookie.Build(Context);            //删除之前的cookie确认信息            Delete(Options.ConsentCookie.Name, cookieOptions);            _logger.ConsentWithdrawn();        }        _hasConsent = false;    }    //返回跟踪cookie的字符串值    public string CreateConsentCookie()    {        var key = Options.ConsentCookie.Name;        var value = ConsentValue;        var options = Options.ConsentCookie.Build(Context);        ApplyAppendPolicy(ref key, ref value, options);        var setCookieHeaderValue = new Net.Http.Headers.SetCookieHeaderValue(            Uri.EscapeDataString(key),            Uri.EscapeDataString(value))            {                Domain = options.Domain,                Path = options.Path,                Expires = options.Expires,                MaxAge = options.MaxAge,                Secure = options.Secure,                SameSite = (Net.Http.Headers.SameSiteMode)options.SameSite,                HttpOnly = options.HttpOnly            };        return setCookieHeaderValue.ToString();    }}

CookiePolicyOptions类的功能

还记得UseCookiePolicy方法有一个参数的重载版本吗?没错,这个参数就是类型。

通过配置CookiePolicyOptions我们可以设置一些全局的cookie约定信息,并允在每次添加、删除cookie时触发指定的方法已完成一些特殊的cookie配置。CookiePolicyOptions代码如下:

public class CookiePolicyOptions{    //设置全局cookie通用配置    public SameSiteMode MinimumSameSitePolicy { get; set; } = SameSiteMode.Lax;    public HttpOnlyPolicy HttpOnly { get; set; } = HttpOnlyPolicy.None;    public CookieSecurePolicy Secure { get; set; } = CookieSecurePolicy.None;    //设置gdpr在浏览器中保存的cookie信息    public CookieBuilder ConsentCookie { get; set; } = new CookieBuilder()    {        Name = ".AspNet.Consent",        Expiration = TimeSpan.FromDays(365),        IsEssential = true,    };    //是否需要用户确认才能将部分cookie发送到客户端    public Func
CheckConsentNeeded { get; set; } //当添加cookie时触发该事件 public Action
OnAppendCookie { get; set; } //当删除cookie时触发该事件 public Action
OnDeleteCookie { get; set; }}

该类是中的一个重要类,我需要的cookie修改监控,gdrp配置等都需要靠该类实现。

总结

  1. cookie策略通过继承IResponseCookies接口,可以实现添加、删除的功能
  2. 通过CookiePolicyOptions类,我们可以修改cookie的全局配置,并在添加、删除cookie时接受到通知,然后做一些你希望做的任何事情
  3. cookie策略通过继承ITrackingConsentFeature接口,可以实现检索、设置cookie的跟踪配置,改配置主要用于

转载地址:http://aennx.baihongyu.com/

你可能感兴趣的文章
Google Gson 使用简介
查看>>
leetcode先刷_Path Sum
查看>>
Windows Directory ACL Security Check By ACL Baseline
查看>>
Socket方法LAN多线程文件传输
查看>>
记录下帮助一位网友解决的关于android子控件的onTouch或onClick和父OnTouch 冲突的问题。...
查看>>
【未解决】CImage::Save / Load 导致“线程 0xc224 已退出,返回值为 1 (0x1)”
查看>>
c3p0数据库连接池
查看>>
基于注解的 Spring MVC 简单入门
查看>>
linux移植简介[MS2]
查看>>
bnu 34986 Football on Table(数学+暴力)
查看>>
jquery mobile
查看>>
使用cstdiofile在vs2010中无法写入中文的问题
查看>>
增益 Gain 分贝 dB
查看>>
Android GUI之View事件处理
查看>>
** poj Y2K Accounting Bug 2586
查看>>
LeetCode——Missing Number
查看>>
so文件成品评论【整理】
查看>>
Android之发送短信的两种方式
查看>>
Android开发系列(十九个):至SimpleAdapter设置样式
查看>>
Facebook ios sdk 3.10 openActiveSessionWithReadPermissions doesn't callback using web login
查看>>