Detailed explanation Asp.Net Cookie policy in core
This article mainly introduces Microsoft.AspNetCore.CookiePolicy The role of this class library.
Function introduction
- Implement IResponseCookies interface, add custom control methods when adding and deleting cookies, and support global cookie property settings.
- realization CookieOptions.IsEssential That identifies whether the current property is required or bypasses the ITrackingConsentFeature.
- 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 IResponseCookiesFeature,ITrackingConsentFeature 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, IResponseCookies,ITrackingConsentFeature , 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
- cookie policy can be added and deleted by inheriting IResponseCookies interface
- 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
- By inheriting ITrackingConsentFeature interface, cookie policy can retrieve and set cookie tracking configuration. The configuration change is mainly used for GDPR