ASP.NET Core 认证与授权[2]:Cookie认证 (6)

那么怎么做呢?在前面注册Cookie认证时,使用的AddCookie方法中,其CookieAuthenticationOptions参数还可以设置一个ITicketStore类型的SessionStore属性,我们可以通过实现该接口来自定义Cookie的存取方式,在这里,使用本地缓存来实现:

首先添加Microsoft.Extensions.Caching.Memory的Package引用:

dotnet add package Microsoft.Extensions.Caching.Memory --version 2.0.0

然后,定义MemoryCacheTicketStore类:

public class MemoryCacheTicketStore : ITicketStore { private const string KeyPrefix = "CSS-"; private IMemoryCache _cache; public MemoryCacheTicketStore() { _cache = new MemoryCache(new MemoryCacheOptions()); } public async Task<string> StoreAsync(AuthenticationTicket ticket) { var key = KeyPrefix + Guid.NewGuid().ToString("N"); await RenewAsync(key, ticket); return key; } public Task RenewAsync(string key, AuthenticationTicket ticket) { var options = new MemoryCacheEntryOptions(); var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc.HasValue) { options.SetAbsoluteExpiration(expiresUtc.Value); } options.SetSlidingExpiration(TimeSpan.FromHours(1)); _cache.Set(key, ticket, options); return Task.FromResult(0); } public Task<AuthenticationTicket> RetrieveAsync(string key) { _cache.TryGetValue(key, out AuthenticationTicket ticket); return Task.FromResult(ticket); } public Task RemoveAsync(string key) { _cache.Remove(key); return Task.FromResult(0); } }

将MemoryCacheTicketStore配置到CookieAuthenticationOptions中:

.AddCookie(options => { options.SessionStore = new MemoryCacheTicketStore(); });

再次重新登录,响应如下:

HTTP/1.1 302 Found Location: /profile Set-Cookie: .AspNetCore.Cookies=CfDJ8B4XRZETkRhMt3mT9VduB8JOI85seEY347RswRzSiL_BQlJTb4JeFqpJzXNW8xOH1CwUKjwsx4CJWyMV5Wwq61IV0Kz4If0LmmJpEicZi2uxmyE2jcCXw_IRaPOaP0eJYM-DkpsjlA_Qu9knFxrpGQaI_BuRbUbbVhy62V5vjwMzoSewmQiPblS1PbPiqXfjAGmF_ZaSM40kwNOboAP_SMoJjX0AtEzmsUqECWFPZLxLoOJJ10Kz16cnSjtxha_KXY7i8f95jVbnX3cj79-GQ5iXnRePBBR_2LsXI5eDW_6E; path=http://www.likecs.com/; samesite=lax; httponly

这样,Cookie中的值就非常简短了(由于其还包含AuthenticationProperties序列化后的值,并没有想象中的短),并且Cookie中的值不会再随着我们设置的Claims的增加而变长,在分布式环境下则可以使用分布式缓存来保存。

Reacting to back-end changes

对于认证系统,身份令牌都会有一个有效期的概念,而Cookie认证中默认有效期是14天,因此只要浏览器没有清除Cookie,并且Cookie没有过期,便么就一直可以验证通过。但是,如果用户修改了密码,我们希望该Cookie失效,或者是用户更新了Claims的信息时,我们希望重新生成Cookie,否则我们取到的还是旧的Claims信息。那么,该怎么做呢?

对此,网上比较流行的做法是在用户数据库中添加一个安全字段,当用户修改了一些安全性信息时,便更新该字段,并在Claim中加入此字段,一起写入到Cookie中,验证时便可以判断该字段是否与数据库一致,若不一致则验证失败或重新生成:

public static class LastChangedValidator { public static async Task ValidateAsync(CookieValidatePrincipalContext context) { var userRepository = context.HttpContext.RequestServices.GetRequiredService<IUserRepository>(); var userPrincipal = context.Principal; string lastChanged = (from c in userPrincipal.Claims where c.Type == "LastUpdated" select c.Value).FirstOrDefault(); if (string.IsNullOrEmpty(lastChanged) || !userRepository.ValidateLastChanged(userPrincipal, lastChanged)) { // 1. 验证失败 等同于 Principal = principal; context.RejectPrincipal(); // 2. 验证通过,并会重新生成Cookie。 context.ShouldRenew = true; } } }

如上,1 和 2 两种方式,我们可以根据实际情况选择一种,而不应该同时存在。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwjjjf.html