eShopOnContainers 知多少[3]:Identity microservice


首先感謝曉晨Master和EdisonChou的審稿!也感謝正在閱讀的您!

引言

通常,服務所公開的資源和 API 必須僅限受信任的特定用戶和客戶端訪問。那進行 API 級別信任決策的第一步就是身份認證——確定用戶身份是否可靠。

在微服務場景中,身份認證通常統一處理。一般有兩種實現形式:

  1. 基於API 網關中心化認證:要求客戶端必須都通過網關訪問微服務。(這就要求提供一種安全機制來認證請求是來自於網關。)
    基於API 網關中心化認證

  2. 基於安全令牌服務(STS)認證:所有的客戶端先從STS獲取令牌,然后請求時攜帶令牌完成認證。
    基於安全令牌服務(STS)認證

而本節所講的Identity microservice就是使用第二種身份認證方式。

服務簡介

Identity microservice 主要用於統一的身份認證和授權,為其他服務提供支撐。

提到認證,大家最熟悉不過的當屬Cookie認證了,它也是目前使用最多的認證方式。但Cookie認證也有其局限性:不支持跨域、移動端不友好等。而從當前的架構來看,需要支持移動端、Web端、微服務間的交叉認證授權,所以傳統的基於Cookie的本地認證方案就行不通了。我們就需要使用遠程認證的方式來提供統一的認證授權機制。
而遠程認證方式當屬:OAuth2.0和OpenID Connect了。借助OAuth2.0和OpenID Connect即可實現類似下圖的認證體系:

而如何實現呢,借助:

  1. ASP.NET Core Identity
  2. IdentityServer4

基於Cookie的認證和基於Token的認證的差別如下所示:

Cookie-Based Auth VS Token-Based Auth

架構模式

該微服務作為支撐服務,並沒有選擇復雜的架構模式,使用了MVC單層架構,使用EF Core ORM框架用於數據持久化,SQL Server數據庫。使用Autofac IOC框架替換了默認依賴注入框架。

項目結構如下所示:
Identity.API 項目結構

核心技術選型:

  1. MVC單層架構
  2. EF Core
  3. ASP.NET Core Identity
  4. IdentityServer4
  5. SQL Server 數據庫
  6. Autofac

PS:對ASP.NET Core Identity、IdentityServer4以及OAuth2.0不了解的,請先行閱讀文末參考資料補課!!!

下面就着重講解ASP.NET Core Identity和IdentityServer4在本服務中的使用。

ASP.NET Core Identity && IdentityServer4簡介

ASP.NET Core Identity用於構建ASP.NET Core Web應用程序的成員資格系統,包括成員資格,登錄和用戶數據(包括登錄信息、角色和聲明)。
ASP.NET Core Identity封裝了User、Role、Claim等身份信息,便於我們快速完成登錄功能的實現,並且支持第三方登錄(Google、Facebook、QQ、Weixin等,支持開箱即用[第三方身份提供商列表]),以及雙重驗證,同時內置支持Bearer 認證(令牌認證)。

雖然ASP.NET Core Identity已經完成了絕大多數的功能,且支持第三方登錄(第三方為其用戶頒發令牌),但若要為本地用戶頒發令牌,則需要自己實現令牌的頒發和驗證邏輯。換句話說,我們需要自行實現OpenId Connect協議。

OpenID Connect 1.0 是基於OAuth 2.0協議之上的簡單身份層,它允許客戶端根據授權服務器的認證結果最終確認終端用戶的身份,以及獲取基本的用戶信息。

而IdentityServer4就是為ASP.NET Core量身定制的實現了OpenId Connect和OAuth2.0協議的認證授權中間件。IdentityServer4在ASP.NET Core Identity的基礎上,提供令牌的頒發驗證等。

認證流程簡介

在ASP.NET Core中使用的是基於申明(Claim)的認證,而什么是申明(Cliam)呢?

Claim 是關於一個人或組織的某個主題的陳述,比如:一個人的名稱,角色,個人喜好,種族,特權,社團,能力等等。它本質上就是一個鍵值對,是一種非常通用的保存用戶信息的方式,可以很容易的將認證和授權分離開來,前者用來表示用戶是/不是什么,后者用來表示用戶能/不能做什么。在認證階段我們通過用戶信息獲取到用戶的Claims,而授權便是對這些的Claims的驗證,如:是否擁有Admin的角色,姓名是否叫XXX等等。

認證主要與以下幾個核心對象打交道:

  1. Claim(身份信息)
  2. ClaimsIdentity(身份證)
  3. ClaimsPrincipal (身份證持有者)
  4. AuthorizationToken (授權令牌)
  5. IAuthenticationScheme(認證方案)
  6. IAuthenticationHandler(與認證方案對應的認證處理器)
  7. IAuthenticationService (向外提供統一的認證服務接口)

那其認證流程是怎樣的呢?

用戶打開登錄界面,輸入用戶名密碼先行登錄,服務端先行校驗用戶名密碼是否有效,有效則返回用戶實例(User),這時進入認證准備階段,根據用戶實例攜帶的身份信息(Claim),創建身份證(ClaimsIdentity),然后將身份證交給身份證持有者(ClaimsPrincipal)持有。接下來進入真正的認證階段,根據配置的認證方案(IAuthenticationScheme),使用相對應的認證處理器(IAuthenticationHandler)進行認證 。認證成功后發放授權令牌(AuthorizationToken)。該授權令牌包含后續授權階段需要的全部信息。

授權流程簡介

授權就是對於用戶身份信息(Claims)的驗證,,授權又分以下幾種種:

  1. 基於Role的授權
  2. 基於Scheme的授權
  3. 基於Policy的授權

授權主要與以下幾個核心對象打交道:

  1. IAuthorizationRequirement(授權條件)
  2. IAuthorizationService(授權服務)
  3. AuthorizationPolicy(授權策略)
  4. IAuthorizationHandler (授權處理器)
  5. AuthorizationResult(授權結果)

那授權流程是怎樣的呢?

當收到授權請求后,由授權服務(IAuthorizationService)根據資源上指定的授權策略(AuthorizationPolicy)中包含的授權條件(IAuthorizationRequirement),找到相對應的授權處理器(IAuthorizationHandler )來判斷授權令牌中包含的身份信息是否滿足授權條件,並返回授權結果。

核心對象

中間件集成

簡單了解了下認證和授權流程后,我們來了解Identity microservice是如何集成相關中間件的。

1. 首先是映射自定義擴展的User和Role

 // 映射自定義的User,Role
services.AddIdentity<ApplicationUser, IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()//配置使用EF持久化存儲
    .AddDefaultTokenProviders();//配置默認的TokenProvider用於變更密碼和修改email時生成Token

2. 配置IdentityServer服務

// Adds IdentityServer
services.AddIdentityServer(x =>
{
    x.IssuerUri = "null";
    x.Authentication.CookieLifetime = TimeSpan.FromHours(2);
})
.AddSigningCredential(Certificate.Get())
.AddAspNetIdentity<ApplicationUser>()
.AddConfigurationStore(options =>
{
    options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
     sqlServerOptionsAction: sqlOptions =>
     {
         sqlOptions.MigrationsAssembly(migrationsAssembly);
         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
     });
})
.AddOperationalStore(options =>
{
    options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString,
     sqlServerOptionsAction: sqlOptions =>
     {
         sqlOptions.MigrationsAssembly(migrationsAssembly);
         //Configuring Connection Resiliency: https://docs.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency 
         sqlOptions.EnableRetryOnFailure(maxRetryCount: 15, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
     });
})
.Services.AddTransient<IProfileService, ProfileService>();

IdentityServer默認直接在內存中存儲配置數據(客戶端和資源)和操作數據(令牌,代碼和和用戶的授權信息consents)。這顯然在生產環境是不合適的,如果服務所在主機宕機,那么內存中的數據就會丟失,所以有必要持久化到數據庫。
其中AddConfigurationStoreAddOperationalStore擴展方法就是用來來指定配置數據和操作數據基於EF進行持久化。

3. 添加IdentityServer中間件

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
     // .....
    // Adds IdentityServer
    app.UseIdentityServer();
}

4. 預置種子數據

從已知的體系結構來說,我們需要預置Client和Resource:

  1. Client
public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
    return new List<Client>
    {
        // SPA OpenId Client Client(Implicit)
        new Client
        // Xamarin Client(Hybrid)
        new Client
        // MVC Client(Hybrid)
        new Client
        // MVC TEST Client(Hybrid)
        new Client
        // Locations Swagger UI(Implicit)
        new Client
        // Marketing Swagger UI(Implicit)
        new Client
        // Basket Swagger UI(Implicit)
        new Client
        // Ordering Swagger UI(Implicit)
        new Client
        // Mobile Shopping Aggregattor Swagger UI(Implicit)
        new Client
        // Web Shopping Aggregattor Swagger UI(Implicit)
        new Client
    };
}
  1. IdentityResources
public static IEnumerable<IdentityResource> GetResources()
{
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}
  1. ApiResources
public static IEnumerable<ApiResource> GetApis()
{
    return new List<ApiResource>
    {
        new ApiResource("orders", "Orders Service"),
        new ApiResource("basket", "Basket Service"),
        new ApiResource("marketing", "Marketing Service"),
        new ApiResource("locations", "Locations Service"),
        new ApiResource("mobileshoppingagg", "Mobile Shopping Aggregator"),
        new ApiResource("webshoppingagg", "Web Shopping Aggregator"),
        new ApiResource("orders.signalrhub", "Ordering Signalr Hub")
    };
}

5. 遷移數據庫上下文

下面就把提前在代碼預置的種子數據遷移到數據庫中,我們如何做呢?IdentityServer為配置數據和操作數據分別定義了DBContext用於持久化,配置數據對應ConfigurationDbContext,操作數據對應PersistedGrantDbContext。代碼如下所示:

public static void Main(string[] args)
{
    BuildWebHost(args)
        .MigrateDbContext<PersistedGrantDbContext>((_, __) => { })//遷移操作數據庫
        .MigrateDbContext<ApplicationDbContext>((context, services) =>
        {
            var env = services.GetService<IHostingEnvironment>();
            var logger = services.GetService<ILogger<ApplicationDbContextSeed>>();
            var settings = services.GetService<IOptions<AppSettings>>();

            new ApplicationDbContextSeed()
                .SeedAsync(context, env, logger, settings)
                .Wait();
        })//遷移用戶數據庫
        .MigrateDbContext<ConfigurationDbContext>((context,services)=> 
        {
            var configuration = services.GetService<IConfiguration>();

            new ConfigurationDbContextSeed()
                .SeedAsync(context, configuration)
                .Wait();
        })//遷移配置數據庫
        .Run();
}

至此,本服務的核心代碼已解析完畢。

最終的生成的數據庫如下圖所示:
IdentityDb

最后

本文從業務和技術上對本服務進行剖析,介紹了其技術選型,並緊接着簡要介紹了ASP.NET Core Identity和IdentityServer4,最后分析源碼,一步步揭開其神秘的面紗。至於客戶端和其他微服務服務如何使用Identity microservice進行認證和授權,我將在后續文章再行講解。

如果對ASP.NET Core Idenity和IdentityServer4不太了解,建議大家博客園閱讀雨夜朦朧曉晨MasterSavorboard
的博客進行系統學習后,再重讀本文,相信你對Identity microservice的實現機制豁然開朗。

參考資料

雨夜朦朧 -- ASP.NET Core 認證與授權:初識認證/授權
Savorboard -- ASP.NET Core 之 Identity 入門(一)
曉晨Master -- IdentityServer(14)- 通過EntityFramework Core持久化配置和操作數據
IdentityServer4 知多少
OAuth2.0 知多少
.NET Core微服務之基於Ocelot+IdentityServer實現統一驗證與授權


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM