Detailed explanation Microsoft.AspNetCore.CookiePolicy

Original text: Detailed explanation Microsoft.AspNetCore.CookiePolicy

Detailed explanation Asp.Net Cookie policy in core

catalog

This article mainly introduces Microsoft.AspNetCore.CookiePolicy The role of this class library.

Function introduction

  1. Implement IResponseCookies interface, add custom control methods when adding and deleting cookies, and support global cookie property settings.
  2. realization CookieOptions.IsEssential That identifies whether the current property is required or bypasses the ITrackingConsentFeature.
  3. Implement ITrackingConsentFeature interface, which is mainly to add and retrieve user confirmation settings to cookie s.

Use Cookie policy

Asp.Net  Core is a highly componentized framework. Many functions, such as authorization, authentication and call back status, are introduced through middleware Microsoft.AspNetCore.CookiePolicy Extension is also introduced through middleware.

Add the following code to the project's Startup:

public class Startup
{
	public void Configure(IApplicationBuilder app)
    {
    	...
    	//cookie policy provides two overloaded versions of usecookie policy
    	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();
    }
}

Start with usecookie policy method

open CookiePolicyAppBuilderExtensions File, you can see that there are two overloaded versions of UseCookiePolicy method. Let's start with this nonparametric UseCookiePolicy. In the nonparametric method, we add a CookiePolicyMiddleware middleware through UseMiddleware method, as shown in the following code:

//C ා extension class, which can add extension methods to the existing class library
public static class CookiePolicyAppBuilderExtensions
{
  	//Add extension methods for iaapplicationbuilder
    public static IApplicationBuilder UseCookiePolicy(this IApplicationBuilder app)
    {
        ...
        //Add middleware for http pipeline
        return app.UseMiddleware<CookiePolicyMiddleware>();
    }	    
}

Let's look at Middleware CookiePolicyMiddleware What has been done.Explain Middleware in detail

adopt context.Features.Set Method, set the IResponseCookiesFeatureITrackingConsentFeature Implementation class of

public class CookiePolicyMiddleware
{
    public Task Invoke(HttpContext context)
    {
        var feature = context.Features.Get<IResponseCookiesFeature>() ?? new ResponseCookiesFeature(context.Features);
        var wrapper = new ResponseCookiesWrapper(context, Options, feature, _logger);
        //In this class, we mainly look at the following two sets   
        context.Features.Set<IResponseCookiesFeature>(new CookiesWrapperFeature(wrapper));
        //Implementation of gdrp
        context.Features.Set<ITrackingConsentFeature>(wrapper);

        return _next(context);
    }
	
    //Implementation of IResponseCookiesFeature
    private class CookiesWrapperFeature : IResponseCookiesFeature
    {
        public CookiesWrapperFeature(ResponseCookiesWrapper wrapper)
        {
            Cookies = wrapper;
        }

        public IResponseCookies Cookies { get; }
    }
}

You may notice context.Features This object, please move if you don't understandExplain Features.

Through the Set method, the implementation of IReponseCookiesFeature is Set to CookiesWrapperFeature, which has a parameter type of RespnseCookiesWrapper And this class implements two interfaces, IResponseCookiesITrackingConsentFeature , let's introduce the functions of these two interfaces.

Implement iresponsecokies interface

Through the ireponsecokies interface responsecooieswrapper, the Append and Delete methods are rewritten. Next, let's see how it is implemented:

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)
    {
    	//If we bind the OnAppendCookie event, it will trigger every time we add a 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));
        }
    	//Use global cookie configuration to modify options
        if (ApplyAppendPolicy(ref key, ref value, options))
        {
            Cookies.Append(key, value, options);
        }
        else
        {
            _logger.CookieSuppressed(key);
        }
    }
    //This method judges that if the cookie cannot be tracked, the same site requirements are set, the read-only cookie is set, the cookie security is set and any other options are set, the implementation of cookie policy is adopted
	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)
    {	
    	//If tracing is enabled or this cookie is required
        var issueCookie = CanTrack || options.IsEssential;
        //Here, change the options configuration to global cookies configuration
        ApplyPolicy(key, options);
        //If we bind to add cookie event
        if (Options.OnAppendCookie != null)
        {
            var context = new AppendCookieContext(Context, options, key, value)
            {
                IsConsentNeeded = IsConsentNeeded,
                HasConsent = HasConsent,
                IssueCookie = issueCookie,
            };
            //Here we perform the method we bind in the setup
            Options.OnAppendCookie(context);
    
            key = context.CookieName;
            value = context.CookieValue;
            //By setting context.IssueCookie true to write cookies to browser cookies
            issueCookie = context.IssueCookie;
        }
    
        return issueCookie;
    }
    //The implementation of the Delete method is the same as that of the Append method. For details, see the comments in the Append method
    public void Delete(string key)
    {
		...
    }
    
    public void Delete(string key, CookieOptions options)
    {
 		...
    }
    //Replace the properties corresponding to the options parameter with the global cookie configuration
    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()}");
        }
    }
}

Through the code, we can see that setting the properties of the OnAppendCookie and OnDeleteCookie of the cookiepolicyoptions object can trigger the call of the modified method every time the cookie is added or deleted. Generally, we can do some global filtering and configuration in it. Finally, we need to set CookiePolicyOptions.IssueCookie To confirm that you want to change the cookie Send to browser.

Implement ITrackingConsentFeature interface

This interface defines the situation in which cookies will be tracked, and the corresponding cookie value will be set in the browser.

public interface ITrackingConsentFeature
{
	bool IsConsentNeeded { get; }

	bool HasConsent { get; }

	bool CanTrack { get; }

	void GrantConsent();

	void WithdrawConsent();
	string CreateConsentCookie();
}

internal class ResponseCookiesWrapper : IResponseCookies, ITrackingConsentFeature
{
	//Whether explicit confirmation by user is required
	public bool IsConsentNeeded
    {
        get
        {
            if (!_isConsentNeeded.HasValue)
            {
                _isConsentNeeded = Options.CheckConsentNeeded == null ? false
                //Get whether the user needs to confirm the operation from the CheckConsentNeeded method of CookiePolicyOptions
                    : Options.CheckConsentNeeded(Context);
                _logger.NeedsConsent(_isConsentNeeded.Value);
            }

            return _isConsentNeeded.Value;
        }
    }
	//Judge whether the user has turned on the confirmation before
    public bool HasConsent
    {
        get
        {
            if (!_hasConsent.HasValue)
            {
            	//Get the value of the confirmation cookie from the cookie we set previously
                var cookie = Context.Request.Cookies[Options.ConsentCookie.Name];
                _hasConsent = string.Equals(cookie, ConsentValue, StringComparison.Ordinal);
                _logger.HasConsent(_hasConsent.Value);
            }

            return _hasConsent.Value;
        }
    }
	//Whether to trace. If the browser does not turn on user confirmation or the browser has already confirmed, it is allowed to trace
    public bool CanTrack => !IsConsentNeeded || HasConsent;
	
	//Write trace enable identity to 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;
    }
	//Undo the trace enable ID written in the previous cookie
    public void WithdrawConsent()
    {
        if (HasConsent && !Context.Response.HasStarted)
        {
            var cookieOptions = Options.ConsentCookie.Build(Context);
           	//Delete previous cookie confirmation
            Delete(Options.ConsentCookie.Name, cookieOptions);
            _logger.ConsentWithdrawn();
        }
        _hasConsent = false;
    }
	//Returns the string value of the trace 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();
    }
}

Features of the CookiePolicyOptions class

Remember that usecookie policy method has an overloaded version of a parameter? Yes, this parameter is CookiePolicyOptions Type.

By configuring the cookie policy options, we can set some global cookie contract information and allow the specified method to trigger each time a cookie is added or deleted to complete some special cookie configuration. The CookiePolicyOptions code is as follows:

public class CookiePolicyOptions
{
    //Set global cookie general configuration
    public SameSiteMode MinimumSameSitePolicy { get; set; } = SameSiteMode.Lax;
    public HttpOnlyPolicy HttpOnly { get; set; } = HttpOnlyPolicy.None;
    public CookieSecurePolicy Secure { get; set; } = CookieSecurePolicy.None;
	//Set the cookie information gdpr saves in the browser
    public CookieBuilder ConsentCookie { get; set; } = new CookieBuilder()
    {
        Name = ".AspNet.Consent",
        Expiration = TimeSpan.FromDays(365),
        IsEssential = true,
    };
    //Whether user confirmation is required to send some cookie s to the client
    public Func<HttpContext, bool> CheckConsentNeeded { get; set; }

	//This event is triggered when a cookie is added
    public Action<AppendCookieContext> OnAppendCookie { get; set; }

	//This event is triggered when a cookie is deleted
    public Action<DeleteCookieContext> OnDeleteCookie { get; set; }
}

This class is Microsoft.AspNetCore.CookiePolicy This is an important class in. I need to implement cookie modification monitoring, gdrp configuration, etc.

summary

  1. cookie policy can be added and deleted by inheriting IResponseCookies interface
  2. Through the CookiePolicyOptions class, we can modify the global configuration of cookies, receive notifications when adding or deleting cookies, and then do whatever you want to do
  3. By inheriting ITrackingConsentFeature interface, cookie policy can retrieve and set cookie tracking configuration. The configuration change is mainly used for GDPR

Posted by plasmahba on Fri, 29 May 2020 20:06:14 -0700