AspNetCore3.1_Secutiry源碼解析_2_Authentication_核心對象


title: "AspNetCore3.1_Secutiry源碼解析_2_Authentication_核心流程"
date: 2020-03-18T21:19:15+08:00
draft: false
---架構

系列文章目錄

  • 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_核心項目
  • AspNetCore3.1_Secutiry源碼解析_9_Authorization_Policy

依賴注入

框架提供了三個依賴注入重載方法。框架

//注入認證服務
services.AddAuthentication();

//注入認證服務並制定默認架構名
services.AddAuthentication("Cookies");

//注入認證服務並設置配置項
services.AddAuthentication(config => 
{
});

看看注入代碼async

public static AuthenticationBuilder AddAuthentication(this IServiceCollection services)
    {
        if (services == null)
        {
            throw new ArgumentNullException(nameof(services));
        }

        services.AddAuthenticationCore();
        services.AddDataProtection();
        services.AddWebEncoders();
        services.TryAddSingleton<ISystemClock, SystemClock>();
        return new AuthenticationBuilder(services);
    }

AddAuthenticationCore注入了認證服務的核心對象。這個方法在Authentication.Core項目,這個項目定義了認證服務的核心對象,在Authentication.Abstractions項目中定義了核心接口。ide

image

AddAuthenticationCore方法注入了IAuthenticationService,IClaimsTransformation,IAuthenticationHandlerProvider,IAuthenticationSchemeProvideroop

public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)
{
    if (services == null)
    {
        throw new ArgumentNullException(nameof(services));
    }

    services.TryAddScoped<IAuthenticationService, AuthenticationService>();
    services.TryAddSingleton<IClaimsTransformation, NoopClaimsTransformation>(); // Can be replaced with scoped ones that use DbContext
    services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();
    services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();
    return services;
}

IAuthenticationService

認證服務,定義了五個方法ui

  • AuthenticateAsync: 認證
  • ChallengeAsync:挑戰,校驗認證
  • ForbidAsync:禁止認證
  • SignInAsync:登入
  • SignOutAsync:登出
classDiagram class IAuthenticationService{ +AuthenticateAsync(HttpContext context, string scheme) +ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) +ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) +SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) +SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) }

經過AuthenticateAsync方法源代碼能夠看到,AuthenticateService只是作了控制器的角色,校驗schema,根據schema獲取handler,主要的認證邏輯是由handler處理。其餘的方法基本也是這樣的邏輯。this

public virtual async Task<AuthenticateResult> AuthenticateAsync(HttpContext context, string scheme)
{
    if (scheme == null)
    {
        var defaultScheme = await Schemes.GetDefaultAuthenticateSchemeAsync();
        scheme = defaultScheme?.Name;
        if (scheme == null)
        {
            throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
        }
    }

    var handler = await Handlers.GetHandlerAsync(context, scheme);
    if (handler == null)
    {
        throw await CreateMissingHandlerException(scheme);
    }

    var result = await handler.AuthenticateAsync();
    if (result != null && result.Succeeded)
    {
        var transformed = await Transform.TransformAsync(result.Principal);
        return AuthenticateResult.Success(new AuthenticationTicket(transformed, result.Properties, result.Ticket.AuthenticationScheme));
    }
    return result;
}

IClaimsTransformation

classDiagram class IClaimsTransformation{ +TransformAsync(ClaimsPrincipal principal) }

該接口只有一個方法,用於轉換Claims。默認注入的NoopClaimsTransformation,不會作任何操做。若是須要對Claims作一些處理,實現IClaimsTransformation並覆蓋注入就能夠了。code

public class NoopClaimsTransformation : IClaimsTransformation
{
    /// <summary>
    /// Returns the principal unchanged.
    /// </summary>
    /// <param name="principal">The user.</param>
    /// <returns>The principal unchanged.</returns>
    public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        return Task.FromResult(principal);
    }
}

IAuthenticationHandlerProvider

classDiagram class IAuthenticationHandlerProvider{ +GetHandlerAsync(HttpContext context, string authenticationScheme) }

上面提到過handler處理了主要的認證業務邏輯,這個接口能夠根據schema獲取handler。
orm

IAuthenticationSchemeProvider

classDiagram class IAuthenticationSchemeProvider{ +GetAllSchemesAsync() +GetSchemeAsync(string name) +GetDefaultAuthenticateSchemeAsync() +GetDefaultChallengeSchemeAsync() +GetDefaultForbidSchemeAsync() +GetDefaultSignInSchemeAsync() +GetDefaultSignOutSchemeAsync() +AddScheme(AuthenticationScheme scheme) +RemoveScheme(string name) +GetRequestHandlerSchemesAsync() }

該接口主要定義了一些schema的操做方法。中間件

AuthenticationScheme主要有三個屬性,經過HandlerType與handler創建了關聯。

classDiagram class AuthenticationScheme{ Name DisplayName HandlerType }

認證流程

graph TD A(AuthenticationOptions定義五個認證動做的Schema) A --> B1(Authenticate) A --> B2(Challenge) A --> B3(Forbid) A --> B4(SignIn) A --> B5(SingOut) C(IAuthenticationSchemeProvider獲取Schema) B1 --> C B2 --> C B3 --> C B4 --> C B5 --> C C --> D(IAuthenticationHandlerProvider獲取Schema對應的Handler) D --> E(處理請求)

其餘

除了核心對象,還注入了用於數據保護和解碼的輔助對象

services.AddDataProtection();
services.AddWebEncoders();

Authentication中間件

中間件會優先在容器中找IAuthenticationRequestHandler的實現,若是handler不爲空的話,則執行handler的HandleRequestAsync方法。IAuthenticationRequestHandler一般在遠程認證(如:OAuth, OIDC等)中使用。

若是沒有IAuthenticationRequestHandler的實現,則會找默認schema,執行默認schema對應handler的AuthenticationAsync方法,認證成功後,給HttpContext的User對象賦值。

public async Task Invoke(HttpContext context)
    {
        context.Features.Set<IAuthenticationFeature>(new AuthenticationFeature
        {
            OriginalPath = context.Request.Path,
            OriginalPathBase = context.Request.PathBase
        });

        // Give any IAuthenticationRequestHandler schemes a chance to handle the request
        var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
        foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
        {
            var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
            if (handler != null && await handler.HandleRequestAsync())
            {
                return;
            }
        }

        var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
        if (defaultAuthenticate != null)
        {
            var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
            if (result?.Principal != null)
            {
                context.User = result.Principal;
            }
        }

        await _next(context);
    }
相關文章
相關標籤/搜索