文章目錄
- AspNetCore3.1_Secutiry原始碼決議_1_目錄
- AspNetCore3.1_Secutiry原始碼決議_2_Authentication_核心專案
- AspNetCore3.1_Secutiry原始碼決議_3_Authentication_Cookies
- AspNetCore3.1_Secutiry原始碼決議_4_Authentication_JwtBear
- AspNetCore3.1_Secutiry原始碼決議_5_Authentication_OAuth
- AspNetCore3.1_Secutiry原始碼決議_6_Authentication_OpenIdConnect
- AspNetCore3.1_Secutiry原始碼決議_7_Authentication_其他
- AspNetCore3.1_Secutiry原始碼決議_8_Authorization_授權框架
依賴注入
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder);
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, string authenticationScheme);
AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, Action<CookieAuthenticationOptions> configureOptions);
提供了幾個多載方法,可以使用默認配置,或者通過委托修改配置類CookieAuthenticationOptions的值,
可以定義登錄、登出、拒絕登錄頁面地址、Cookie過期時間、生命周期各階段事件等,
classDiagram class CookieAuthenticationOptions{ CookieBuilder Cookie IDataProtectionProvider DataProtectionProvider bool SlidingExpiration PathString LoginPath PathString LogoutPath PathString AccessDeniedPath CookieAuthenticationEvents Events ISecureDataFormat TicketDataFormat ITicketStore SessionStore TimeSpan ExpireTimeSpan } class AuthenticationSchemeOptions{ string ClaimsIssuer object Events Type EventsType string ForwardDefault string ForwardAuthenticate string ForwardChallenge string ForwardForbid string ForwardSignIn string ForwardSignOut Func ForwardDefaultSelector } CookieAuthenticationOptions-->AuthenticationSchemeOptions如果沒有定義配置,則會使用CookieAuthenticationDefaults定義的默認配置
/// <summary>
/// Default values related to cookie-based authentication handler
/// </summary>
public static class CookieAuthenticationDefaults
{
/// <summary>
/// The default value used for CookieAuthenticationOptions.AuthenticationScheme
/// </summary>
public const string AuthenticationScheme = "Cookies";
/// <summary>
/// The prefix used to provide a default CookieAuthenticationOptions.CookieName
/// </summary>
public static readonly string CookiePrefix = ".AspNetCore.";
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.LoginPath
/// </summary>
public static readonly PathString LoginPath = new PathString("/Account/Login");
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.LogoutPath
/// </summary>
public static readonly PathString LogoutPath = new PathString("/Account/Logout");
/// <summary>
/// The default value used by CookieAuthenticationMiddleware for the
/// CookieAuthenticationOptions.AccessDeniedPath
/// </summary>
public static readonly PathString AccessDeniedPath = new PathString("/Account/AccessDenied");
/// <summary>
/// The default value of the CookieAuthenticationOptions.ReturnUrlParameter
/// </summary>
public static readonly string ReturnUrlParameter = "ReturnUrl";
}
注冊當前schema的處理器類為CookieAuthenticationHandler
處理器類的結構
主干邏輯是層層繼承來實作的,CookieAuthenticationHandler主要是重寫了父類的五個認證動作的Handle方法來實作自己的處理邏輯,
classDiagram class CookieAuthenticationHandler{ HandleAuthenticateAsync() HandleSignInAsync() HandleSignOutAsync() HandleForbiddenAsync() HandleChallengeAsync() FinishResponseAsync() } class SignInAuthenticationHandler{ SignInAsync() HandleSignInAsync() } class IAuthenticationSignInHandler{ SignIn() HandleSignIn() } class SignOutAuthenticationHandler{ SignOutAsync() HandleSignOutAsync() } class IAuthenticationSignOutHandler{ SighOut() HandleSignOut() } class AuthenticationHandler{ AuthenticationScheme Scheme TOptions Options HttpContext Context HttpRequest Request HttpResponse Response PathString OriginalPath PathString OriginalPathBase ILogger Logger UrlEncoder UrlEncoder ISystemClock Clock object Events string ClaimsIssuer string CurrentUri +Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) +Task AuthenticateAsync() +Task ChallengeAsync(AuthenticationProperties properties) +Task ForbidAsync(AuthenticationProperties properties) } class IAuthenticationHandler{ HandleAsync() } CookieAuthenticationHandler-->SignInAuthenticationHandler SignInAuthenticationHandler-->IAuthenticationSignInHandler SignInAuthenticationHandler-->SignOutAuthenticationHandler SignOutAuthenticationHandler-->IAuthenticationSignOutHandler SignOutAuthenticationHandler-->AuthenticationHandler AuthenticationHandler-->IAuthenticationHandler處理器類詳解
HandleSignInAsync - 處理登錄
- 業務方校驗完用戶之后之后,構造ClaimsPrincipal物件傳入SignIn方法,如果user為null則拋出例外
- IssuedUtc如果未指定的話則使用當前時間,ExpiresUtc過期時間如果沒有指定的話則用IssuedUtc和ExpireTimeSpan計算出過期時間
- 觸發SigningIn事件
- 構造AuthenticationTicket憑證
- 如果SessionStore不為空,將憑證資訊存入SessionStore
- TicketDataFormat對ticket進行加密
- CookieManager將t加密后的資訊寫入cookie
- 觸發SignedIn事件
- 如果LoginPath有值并且等于OriginalPath,則需要跳轉,跳轉地址在Properties.RedirectUri
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 = https://www.cnblogs.com/holdengong/p/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.AuthenticationSchemeSignedIn(Scheme.Name);
}
HandleAuthentication - 處理認證
- 從Cookie中讀取憑證:首先TicketDataFormat類將Cookie解碼,如果SessionStore不為null,說明解碼值是只是session的key,從SessionStore中取出值,
- 構建CookieValidatePrincipalContext,觸發ValidatePrincipal事件
- 如果ShouldRenew位true,則會重繪cookie(ShoudRenew默認為false,可以通過訂閱ValidatePrincipal事件來修改)
- 認證成功,發放憑證AuthenticationTicket,包括context.Principal, context.Properties, Scheme.Name這些資訊
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
var result = await EnsureCookieTicket();
if (!result.Succeeded)
{
return result;
}
var context = new CookieValidatePrincipalContext(Context, Scheme, Options, result.Ticket);
await Events.ValidatePrincipal(context);
if (context.Principal == null)
{
return AuthenticateResult.Fail("No principal.");
}
if (context.ShouldRenew)
{
RequestRefresh(result.Ticket, context.Principal);
}
return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name));
}
HandleSignOutAsync - 處理登出
- 獲取憑證
- SessionStore不為null的話則從SessionStore移除會話
- 觸發SigningOut事件
- CookieManager洗掉cookie
- 如果源地址是LogoutPath,則跳轉到登出后地址
protected async override Task HandleSignOutAsync(AuthenticationProperties properties)
{
properties = properties ?? new AuthenticationProperties();
_signOutCalled = true;
// Process the request cookie to initialize members like _sessionKey.
await EnsureCookieTicket();
var cookieOptions = BuildCookieOptions();
if (Options.SessionStore != null && _sessionKey != null)
{
await Options.SessionStore.RemoveAsync(_sessionKey);
}
var context = new CookieSigningOutContext(
Context,
Scheme,
Options,
properties,
cookieOptions);
await Events.SigningOut(context);
Options.CookieManager.DeleteCookie(
Context,
Options.Cookie.Name,
context.CookieOptions);
// Only redirect on the logout path
var shouldRedirect = Options.LogoutPath.HasValue && OriginalPath == Options.LogoutPath;
await ApplyHeaders(shouldRedirect, context.Properties);
Logger.AuthenticationSchemeSignedOut(Scheme.Name);
}
HandleForbidAsync -- 處理禁止訪問
如果是ajax請求會回傳403狀態碼,否則跳轉到配置的AccessDeniedPath
protected override async Task HandleForbiddenAsync(AuthenticationProperties properties)
{
var returnUrl = properties.RedirectUri;
if (string.IsNullOrEmpty(returnUrl))
{
returnUrl = OriginalPathBase + OriginalPath + Request.QueryString;
}
var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl);
var redirectContext = new RedirectContext<CookieAuthenticationOptions>(Context, Scheme, Options, properties, BuildRedirectUri(accessDeniedUri));
await Events.RedirectToAccessDenied(redirectContext);
}
public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToAccessDenied { get; set; } = context =>
{
if (IsAjaxRequest(context.Request))
{
context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = 403;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};
其他
ICookieManager - Cookie管理類
默認實作是ChunkingCookieManager,如果cookie過長,該類會將cookie拆分位多個chunk,
/// <summary>
/// This is used by the CookieAuthenticationMiddleware to process request and response cookies.
/// It is abstracted from the normal cookie APIs to allow for complex operations like chunking.
/// </summary>
public interface ICookieManager
{
/// <summary>
/// Retrieve a cookie of the given name from the request.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <returns></returns>
string GetRequestCookie(HttpContext context, string key);
/// <summary>
/// Append the given cookie to the response.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="options"></param>
void AppendResponseCookie(HttpContext context, string key, string value, CookieOptions options);
/// <summary>
/// Append a delete cookie to the response.
/// </summary>
/// <param name="context"></param>
/// <param name="key"></param>
/// <param name="options"></param>
void DeleteCookie(HttpContext context, string key, CookieOptions options);
}
ITicketStore - 實作Cookie持久化
ITicketStore默認是沒有實作的,如果實作該介面并注入的話,可以將cookie持久化,這樣暴露在瀏覽器的只是一個cookie的id,
/// <summary>
/// This provides an abstract storage mechanic to preserve identity information on the server
/// while only sending a simple identifier key to the client. This is most commonly used to mitigate
/// issues with serializing large identities into cookies.
/// </summary>
public interface ITicketStore
{
/// <summary>
/// Store the identity ticket and return the associated key.
/// </summary>
/// <param name="ticket">The identity information to store.</param>
/// <returns>The key that can be used to retrieve the identity later.</returns>
Task<string> StoreAsync(AuthenticationTicket ticket);
/// <summary>
/// Tells the store that the given identity should be updated.
/// </summary>
/// <param name="key"></param>
/// <param name="ticket"></param>
/// <returns></returns>
Task RenewAsync(string key, AuthenticationTicket ticket);
/// <summary>
/// Retrieves an identity from the store for the given key.
/// </summary>
/// <param name="key">The key associated with the identity.</param>
/// <returns>The identity associated with the given key, or if not found.</returns>
Task<AuthenticationTicket> RetrieveAsync(string key);
/// <summary>
/// Remove the identity associated with the given key.
/// </summary>
/// <param name="key">The key associated with the identity.</param>
/// <returns></returns>
Task RemoveAsync(string key);
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/54802.html
標籤:.NET Core
