Microsoft.AspNetCore.Authentication.Cookies from entry to mastery

Original text: Microsoft.AspNetCore.Authentication.Cookies from entry to mastery (2)

Microsoft.AspNetCore.Authentication.Cookies from entry to mastery (2)

catalog

In the last article, we introduced Microsoft.AspNetCore.Authentication . some of the contents of cookies have to be introduced in several parts due to the length problem. We will continue the previous introduction in this part.

Demo source address

Cookie encryption

In the previous Demo, in the browser Cookie AspNetCore.Cookies Is an encrypted string, default Asp.Net The core has encrypted the value. We don't know how to encrypt it (you can find some clues by looking at the source code), If we want to modify the encryption method, there is only one way to set our own implementation through the DataProtectionProvider property:

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);
            //Set up our own data encryption implementation
            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;
        //We can also return multiple instances here
        //return new MyDataProtector(purpose);
        return this;
    }

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

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

MyDataProtector is our custom encryption class. Here we do not implement the Protect and Unprotect methods, but simply do a return operation. The specific encryption method to be used should be given to the person who needs it. Here we just show how to customize the encryption class.

Cookie pseudo encryption (serialization / deserialization)

​ Pseudo encryption refers to the TicketDataFormat property. We have introduced the encryption and decryption methods defined in the IDataProtector interface. Here we introduce the protection and Unprotect methods defined in the ISecureDataFormat interface. The meaning of the methods of the two interfaces is the same from the literal point of view, but from the implementation code point of view, they are not the same at all. The ISecureDataFormat interface does not handle the same things Encryption, to be exact, is used for serialization and deserialization. Let's take a look at it Asp.Net Default implementation in 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();
            //Using our own serialization implementation, the code here looks a bit awkward. On the previous line, we have set the MyDataProtector instance, and here we set it again, because Asp.Net  The core framework itself is a bit of a problem here. This kind of confusing writing can be avoided through dependency injection.
            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);
    }
}

We have defined a serialization class, MyTicketDataFormat. Here, the inherited parent class is ticketdataformat, and ticketdataformat is Asp.Net The default implementation of core, if we do not set options.TicketDataFormat The default serialization class is ticketdataformat. If you need to customize serialization in a real project, you should implement isecuredataformat < authenticationticket > interface instead of being lazy like me. I'm here purely for demonstration. Let's take a look Asp.Net How core is implemented SecureDataFormat Class (SecureDataFormat is the parent of TicketDataFormat). Note here that if you implement the isecuredataformat < authenticationticket > interface, don't forget to inject idataporotector into the constructor. If you really forget, you set the options.DataProtectionProvider =New mydataprotectionprovider(); will not work (I think this is a big hole).

Reduce the size of the Value in the Cookie

By default Asp.Net The implementation of core is to serialize and encrypt the AuthenticationTicket object and store it in the Cookie. Then there will be a hidden danger. With more and more data stored in the AuthenticationTicket object, the Cookie will become larger and larger. However, the browser only gives us 4K of storage space. It is possible that your Cookie will not be able to hold your data stream one day, so it is us to reduce the storage of cookies On the other hand, reducing the amount of Cookie data is also conducive to network transmission.

So how can we reduce the storage space of cookies?! Let's learn from it Asp.Net The implementation principle of Session in core is to write Cookie information into memory, cache server (redis), and then correspond with it through unique identification, so that the storage and transmission volume of Cookie can be greatly reduced.

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());
            //Add Cookie storage implementation
            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);
    }
}

In the above code, we implement the user-defined type MyTicketStore of ITicketStore to define the storage of AuthenticationTicket data. In MyTicketStore, we use dictionary < string, AuthenticationTicket > to store authentication data. In fact, we should use memory or external cache system, such as Redis to store authentication data.

Customize Cookie management features

Cookie management here refers to the ICookieManager interface, which is mainly used to add, delete and obtain cookie information, that is Microsoft.AspNetCore.Authentication.Cookies actually writes cookies to the http header and obtains the entry of cookies from the http header.

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];
    }
}

Our implementation here is very simple. We just call the object on HttpContext to operate the Cookie. There may be some problems in the implementation. For a more secure implementation, please see ChunkingCookieManager.

summary

  1. We introduced how to implement our own encryption method through the IDataProtector interface.
  2. We introduced how to realize our own serialization method through isecuredataformat < authenticationticket > interface, and mentioned that there may be a pit here. When we customize the serialization method, we need to add the IDataProtector parameter to the constructor, otherwise the DataProtectionProvider property may lose its meaning.
  3. We introduce how to implement Session like storage mechanism through the ITicketStore interface to reduce the storage and transmission of cookies in the browser.
  4. We introduced how to realize the custom management function of cookies through the ICookieManager interface.

To be continued

Keywords: Redis Session network

Added by DragonHighLord on Sat, 30 May 2020 06:26:04 +0300