HttpContext.SignInAsync的原理
使用的是Cookie认证那么就是通过Microsoft.AspNetCore.Authentication.Cookies库的CookieAuthenticationHandler类的HandleSignInAsync方法进行处理的。
源码地址:
protected async override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { if (user == null) { throw new ArgumentNullException(nameof(user)); } properties = properties ?? new AuthenticationProperties(); _signInCalled = true; // Process the request cookie to initialize members like _sessionKey. await EnsureCookieTicket(); var cookieOptions = BuildCookieOptions(); var signInContext = new CookieSigningInContext( Context, Scheme, Options, user, properties, cookieOptions); DateTimeOffset issuedUtc; if (signInContext.Properties.IssuedUtc.HasValue) { issuedUtc = signInContext.Properties.IssuedUtc.Value; } else { issuedUtc = Clock.UtcNow; signInContext.Properties.IssuedUtc = issuedUtc; } if (!signInContext.Properties.ExpiresUtc.HasValue) { signInContext.Properties.ExpiresUtc = issuedUtc.Add(Options.ExpireTimeSpan); } await Events.SigningIn(signInContext); if (signInContext.Properties.IsPersistent) { var expiresUtc = signInContext.Properties.ExpiresUtc ?? issuedUtc.Add(Options.ExpireTimeSpan); signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime(); } var ticket = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.Scheme.Name); if (Options.SessionStore != null) { if (_sessionKey != null) { await Options.SessionStore.RemoveAsync(_sessionKey); } _sessionKey = await Options.SessionStore.StoreAsync(ticket); var principal = new ClaimsPrincipal( new ClaimsIdentity( new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) }, Options.ClaimsIssuer)); ticket = new AuthenticationTicket(principal, null, Scheme.Name); } var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding()); Options.CookieManager.AppendResponseCookie( Context, Options.Cookie.Name, cookieValue, signInContext.CookieOptions); var signedInContext = new CookieSignedInContext( Context, Scheme, signInContext.Principal, signInContext.Properties, Options); await Events.SignedIn(signedInContext); // Only redirect on the login path var shouldRedirect = Options.LoginPath.HasValue && OriginalPath == Options.LoginPath; await ApplyHeaders(shouldRedirect, signedInContext.Properties); Logger.SignedIn(Scheme.Name); }
从源码我们可以分析出流程:
根据ClaimsPrincipal的用户信息序列化后通过加密方式进行加密获得ticket。
(默认加密方式是的KeyRingBasedDataProtecto。
源码地址:https://github.com/aspnet/DataProtection)
再通过之前的初始化好的CookieOption再AppendResponseCookie方法进行设置Cookie
最后通过Events.RedirectToReturnUrl进行重定向到ReturnUrl。
Ticket加密
两种设置方式
- CookieAuthenticationOptions.DataProtectionProvider
- CookieAuthenticationOptions.TicketDataFormat
DataProtectionProvider
如果做了集群可以设置到共享文件夹,在第一个启动的应用则会创建如下图的文件
options.DataProtectionProvider = DataProtectionProvider.Create(new DirectoryInfo(@"D:\sso\key"));
TicketDataFormat
重写数据加密方式,本次demo使用了是AES.
options.TicketDataFormat = new TicketDataFormat(new AesDataProtector()); internal class AesDataProtector : IDataProtector { private const string Key = "!@#13487"; public IDataProtector CreateProtector(string purpose) { return this; } public byte[] Protect(byte[] plaintext) { return AESHelper.Encrypt(plaintext, Key); } public byte[] Unprotect(byte[] protectedData) { return AESHelper.Decrypt(protectedData, Key); }