從壹開始 [ Ids4實戰 ] 之三║ 詳解授權持久化 & 使用者資料遷移

老張的哲學發表於2019-05-08

回顧

 哈嘍大家週三好,今天終於又重新開啟 IdentityServer4 的落地教程了,不多說,既然開始了,就要努力做好?。

書接上文,在很久之前的上篇文章《二║ 基礎知識集合 & 專案搭建一》中,我們簡單的說了說 IdentityServer4 是如何呼叫和配置 Token 的,主要是一些入門的基礎知識和概念,也算是第一次嚐鮮了,其實 Ids4 本身沒有那麼神祕,我們只需要知道它是一個豐富的認證框架,又具有很好的可擴充套件性,核心就是如何配置某些客戶端通過該授權服務來包括另一些資源伺服器,供使用者使用。因為中間隔了很長時間了,這裡簡單的回顧下上篇文章說了什麼,下邊這幾個小問題,相信大家腦中都能很快的有一些概念,至少是臉熟了,如果不是很清楚,請翻看上邊的連結,或者官網再看看:

 

1、什麼是資源?什麼是客戶端?什麼是令牌 ?

2、IdentityServer4 主要是授權還是認證 ?

3、OAuth 2.0、OpenID、OpenID Connect三者的關係 ?

4、Ids4 依賴的 Nuget 包是什麼 ?

5、如何快速開發 UI ?

 

 今天這篇文章主要是操作和探索相結合的一篇,我看網上很多教程,基本上是按照官網概念的知識點,配合著 Github 開源地址,講解一遍,知識是懂了,可是要是自己開發起來,還是有很多的疑惑,甚至是根本走不下去,我就從初學者的立場出發,換一個思路,簡單的對其進行探索,希望能讓初學者感興趣,而不會失去學習的衝動,當然,這篇文章會一下子就很完整,需要一段時間的沉澱和完善,我會不定時將學習到的感悟一一補充下來,然後也會和別人討論的心得寫下來,慢慢的將這篇文章逐漸完整起來。希望大家都積極評論,混個臉熟也會挺好,如果一直潛水,可能都不知道你的存在。?

之前的配置都是在記憶體中,下面將如何把這些資料儲存到Sql Server資料庫, 這樣更符合生產環境的要求。好啦,馬上開始今天的內容~~~

 

 (這是簡單處理了下登入頁,要用就要好看些

 

一、基於 EFCore 的持久化操作

1、安裝依賴環境 ——基礎

我們要把所有的資料都儲存下來,採用介面進行建模,我們在 IdentityServer4.EntityFramework Nuget 包中提供這些介面的 EF 實現。

算上我們上篇文章中引用的 IdentityServer4 包,一共這三個 Nuget 依賴環境: 

 

   

2、三個上下文 —— 核心

這一節大家如果看懂的話,一定會對 IdentityServer4 豐富性和可擴充套件性 有特別切身的瞭解和體會!?

 

Z、為什麼要定義多個上下文?

為什麼要多個上下文? 

還記得在第二個系列 DDD 中,我們說到了多個上下文的用途,單個資料庫可以有多個上下文。例如, 如果您的資料庫包含多個資料庫架構, 並且您希望將每個架構作為單獨的自包含區域處理, 則此功能非常有用。在使用多個上下文型別時,您還會看到其他問題, 例如共享實體型別及其從一個上下文傳遞到另一個上下文等。一般來說,設計多個上下文,會使您的設計更乾淨, 並分離不同的功能區域,但同時它的成本也增加了複雜性。框架之所以會設計成多個上下文,是為了更好的擴充套件,我們也可以部署到不同的物理機資料庫中,更好的實現獨立性。

 

在我們的 IdentityServer4-EF 框架中,有三個上下文,準確來說是 2+1 個上下文,為什麼這麼說呢 ?

因為第三個上下文不是官方定義的,是我們自己根據需要進行合理擴充套件的,比如我們可以使用 Asp.Net Core 自帶的 Identity 身份認證機制來實現擴充套件,當然,你也可以自己定義相應的操作,如果你還不懂,就想象一下我們在 Blog.Core 專案中,我們不是自己定義的基於 JWT 的認證授權麼( 使用者表 + 角色表 + 關係表等等 ),但是如果你看我的 DDD 專案的話,應該可以看到我又使用了 Core 官方自帶的 Identity 機制,畢竟結合 EFCore 我們可以很快的實現使用者資料的管理,而不用自己造輪子了。

 

 

我們們這裡先說說前兩個上下文,在我們使用 Ids4 的時候,有兩種型別的資料需要持久化到資料庫中:

1、配置資料(資源、客戶端、身份);//這裡是對應配置上下文 ConfigurationDbContext 

2、IdentityServer在使用時產生的 運算元據(令牌,程式碼和使用者的授權資訊consents);//這裡是對應操作上下文 PersistedGrantDbContext 

這兩個上下文以及對應的資料模型,已經被 IdentityServer4 官方給封裝好了, 我們不需要做額外的操作,直接進行遷移即可。

 

A:ConfigurationDb

ConfigurationDbContext (IdentityServer configuration data) —— 負責資料庫中對客戶端、資源和 CORS 設定的配置儲存;

如果需要從 EF 支援的資料庫載入客戶端、標識資源、API 資源或 CORS 資料 (而不是使用記憶體中配置), 則可以使用配置儲存。此支援提供 IClientStore、IResura Store 和 ICorsPolicyService 擴充套件性點的實現。這些實現使用名為 ConfigurationDbContext 的 dbcontext 派生類對資料庫中的表進行建模。

更多內容請檢視官網 :http://docs.identityserver.io/en/latest/reference/ef.html#configuration-store-support-for-clients-resources-and-cors-settings

具體在  startup 中如何配置,下面會說到。

 

B:PersistedGrantDb

PersistedGrantDbContext (IdentityServer operational data.) -—— 負責儲存同意、授權程式碼、重新整理令牌和引用令牌;

 如果需要從 EF 支援的資料庫 (而不是預設的記憶體資料庫) 載入授權授予、同意和令牌 (重新整理和引用), 則可以使用操作儲存。此支援提供了 IPersistedGrantStore 擴充套件點的實現。實現使用名為 PersistedGrantDbContext  的 dbcontext 派生類對資料庫中的表進行建模。

 

 

C:ApplicationDb

ApplicationDbContext 繼承 IdentityDbContext (Entity Framework database context used for identity.) —— 負責與 asp. net 標識相關的使用者;

這個上下文,就是 IdentityServer4 體現 可擴充套件性 的關鍵點,為什麼說是可擴充套件的,因為這是我們自己定義的,你可以在這裡僅僅定義一個User表,也可以像我們的 Blog.Core 專案中,定義三個表。我們可以在這個上下文中,進行配置,來控制我們的使用者資料,當然這裡看著很簡單,是因為繼承了 NetCore 官方的 Identity 了,可以使用他們的那一套邏輯。

 public class ApplicationUser : IdentityUser
 {
   //可以在這裡擴充套件,下文會說到
 }


 // 定義使用者管理上下文,繼承 NetCore 自帶的 Identity 認證機制,也可以不繼承而自定義表結構。
 public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
 {
     public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
         : base(options)
     {
     }

     protected override void OnModelCreating(ModelBuilder builder)
     {
         base.OnModelCreating(builder);
         // Customize the ASP.NET Identity model and override the defaults if needed.
         // For example, you can rename the ASP.NET Identity table names and more.
         // Add your customizations after calling base.OnModelCreating(builder);
     }
 }

這個上下文,主要是用來處理我們的使用者資料相關的部分:

1、有多少使用者資料和角色資料;

2、哪些使用者,又擁有什麼角色,又對應哪些Claims 宣告。

這一塊更像是我們的平時的許可權管理系統,就是在 User、Role、Claim 之間互動,文章下邊會有一個資料模型圖,會很清晰的看出來各自的對應關係。

我們既然需要用到這幾個上下文,就需要新增他們所對應的服務,彆著急,往下看。 

 

 

3、配置 EF-Ids4 相關服務 —— 必要

 

上邊我們說到了三個上下文,如果我們直接執行遷移命令是會報錯的,比如我們直接遷移 PersistedGrantDbContext 上下文:

 

所以,就需要在專案中配置對應的服務,我們在 startup.cs 啟動檔案中,配置服務 ConfigureService :

配置 EF 運算元據庫,很簡單,這個就按照平時我們使用的正常的方法即可:

 

1、獲取資料庫連線字串:

( 我這裡使用的是讀取檔案的形式,其實就是字串放到檔案裡了,大家自己根據需要做相應調整即可,我這麼是防止密碼洩露。)

 

2、配置資料庫服務:

    var builder = services.AddIdentityServer(options =>
     {
         options.Events.RaiseErrorEvents = true;
         options.Events.RaiseInformationEvents = true;
         options.Events.RaiseFailureEvents = true;
         options.Events.RaiseSuccessEvents = true;
     })

    // Configures IdentityServer to use the ASP.NET Identity implementations of IUserClaimsPrincipalFactory,
    // IResourceOwnerPasswordValidator, and IProfileService. Also configures some of
    // ASP.NET Identity's options for use with IdentityServer (such as claim types to
    // use and authenticaiton cookie settings).

     .AddAspNetIdentity<ApplicationUser>()
     // 新增配置資料(客戶端 和 資源)
     .AddConfigurationStore(options =>
                {
                    options.ConfigureDbContext = b =>
                        b.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                })
     // 新增運算元據 (codes, tokens, consents)
     .AddOperationalStore(options =>
                {
                    options.ConfigureDbContext = b =>
                        b.UseSqlServer(connectionString,
                            sql => sql.MigrationsAssembly(migrationsAssembly));
                    // 自動清理 token ,可選
                    options.EnableTokenCleanup = true;
                });

 

 然後是我們的使用者資料儲存,其實我們可以根據自己的需要,自己建立對應的表,比如我們 Blog.Core 專案中,我們定義了使用者、角色和關係表等資料庫介面,但是這樣的話,可能少一些情況,所以一般情況下,都是採用的 asp.net core 自帶的 Identity 身份驗證,而且對擴充套件性很友好,就比如下文我們就對使用者主表做了自定義擴充套件,繼承了 IdentityUser, ApplicationUser : IdentityUser 

 // 資料庫配置系統應用使用者資料上下文
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); // 啟用 Identity 服務 新增指定的使用者和角色型別的預設標識系統配置 services.AddIdentity<ApplicationUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders();

 

 

4、Migrations 遷移 —— 結果

我這裡提供了兩種方法,至於哪種更好,大家可以自己都試試,我都用過,可能第二種稍微多了一點,不過原理都是一樣的。

方式一:在包控制檯

 一、遷移專案( 使用 Package Manager Console ):
   1、add-migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb 
   2、add-migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
   3、add-migration AppDbMigration -c ApplicationDbContext -o Data
   4、update-database -c PersistedGrantDbContext
5、update-database -c ConfigurationDbContext
6、update-database -c ApplicationDbContext

 

 

 

最後成功的生成了我們需要的資料庫:

 

 

 

方式二:在命令視窗

判斷是否支援命令列遷移,你可以在專案所在的目錄下開啟一個命令 Power shell 並執行命令 dotnet ef, 它應該是這樣的:

 

 

我是在 PowerShell 中操作的:

//1.dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
//2.dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
//3.dotnet ef migrations add AppDbMigration -c ApplicationDbContext -o Data
//4.dotnet run /seed

這裡我就不一一操作了,就是普通的命令執行,生成的資料庫一模一樣的。

但是要注意下,上邊我們在包管理控制檯,僅僅是遷移了資料庫,還沒有進行資料 Seed 生成,不過你看 PowerShell 的命令中的第四步,我使用  dotnet run /seed 的命令,和上邊的不一樣,沒錯,是因為下邊我用了另一種方法,通過程式碼的方法進行 Migrate 了,並且同時還 Seed 了資料,那具體如何操作呢,別慌,第三節我會說到了,現在這個時候,我們已經把資料庫的所有表都已經生成好了,先不著急同步資料,我們先看看到底生成了哪些資料表。

 

 

二、資料庫表的含義(更新中)

 

1、整體資料模型圖

我簡單的做了下整個資料庫的資料模型圖,不同模組用不同的顏色區分,大家也能很清楚的看出來各自對應的關係,一目瞭然,其中配置資料又有三個部分,分別對應的是 客戶端 + 資源 + 身份 。

 

 接下來,我們們針對這三個模組簡單的說一說,因為這裡還沒有資料,說起來可能會比較抽象,這裡先留一個坑,等配合 Vue 客戶端使用了以後,再把完整資料展示出來,可能更好的理解了。

 

2、三個模組表

 

運算元據模組

 

這是 Ids4 官方封裝的第一部分——運算元據。PersistedGrants的資料,在登陸時當你同意請求許可的時候,就會在這個表裡面新增一條資料。

舉個例子,我在 Blog.Vue 專案點選登入,跳轉到 BlogIdp 授權中心,認證成功後,跳轉回來,就會在這個表中多了一條資料(這個功能我下次會開放出來,程式碼已經寫好):

MMD4TtLkVH7sXIwFS/DLZRfLQnUga0XMrBDVuQZWfOw= user_consent c2841087-1b35-4ab4-afd6-f27f80c94e6c blogvuejs 2019-05-08 03:35:30.0000000 NULL {"SubjectId":"c2841087-1b35-4ab4-afd6-f27f80c94e6c","ClientId":"blogvuejs","Scopes":["openid","profile","roles","blog.core.api"],"CreationTime":"2019-05-08T03:35:30Z","Expiration":null}

 

 

 

配置資料模組

 

 

這一大塊,是 Ids4 官方定義的第二模組 —— 配置資料,從這裡我們可以看出來 IdentityServer4 的另一個特性——豐富性,上邊我們們說到了可擴充套件性,它提供了豐富的配置資料結構,從三個方面來給我們定義好了這些資料,而不用我們再去一個一個的去設計,去思考。

我們可以通過這些表結構來儲存我們的服務資料,比如有哪些API資源(Blog.Core.API),哪些客戶端資源,還有用到了哪些資源等等,如果你還不清楚的,可以想想上篇文章中,我們講記憶體模式的 Ids4 的配置,就是這些資料,在下邊的文章中,我們會進行 Seed Data,那個時候我們再進一步看下。

 

具體的資料大家可以看看,截個圖:

 

 

 

使用者資料模組

 

這一塊我們就很清楚了,因為這是我們自定義的使用者資料(當然本專案是直接繼承的 NetCore 的 Identity) ,光從資料庫表名上,我們就知道其中的含義了,就是使用者角色管理。

 

上邊我們們也簡單的說了下資料庫表結構,然後再結合三個上下文,大家應該都能明白各自的含義了,好啦,現在就是萬事俱備,只欠資料了,是時候動手了!

 

三、同步更新其他專案的使用者資料


還記得我們上邊在生成資料庫的時候,我採用了兩個方法,第一種很常規,那接下來就說說第二種中第四步( dotnet run /see ),是如何操作的。

 

1、定義 SeedData 類

首先,需要定義一個 SeedData 類,主要是用來 update-database  和  Seed Data 這兩個作用,因為篇幅的原因,具體的程式碼在 Github 上,大家自行下載檢視即可:

 

 

 

 

2、擴充套件使用者主表資料

 上邊我們們也說到了,我們通過繼承使用 Identity 機制,來擴充套件了使用者管理,很自然的,我們需要用到 IdentityUser ,但是它的這個使用者資料肯定不能滿足我們的需求,所以,這裡又一次體現了 Ids4 可擴充套件性的優點,我們可以自定義使用者主表:

  // Add profile data for application users by adding properties to the ApplicationUser class
  public class ApplicationUser : IdentityUser
  {
      // 自定義屬性
      public string name { get; set; }
      public string RealName { get; set; }
      public int sex { get; set; } = 0;
      public int age { get; set; }
      public DateTime birth { get; set; } = DateTime.Now;
      public string addr { get; set; }
      public bool tdIsDelete { get; set; }
  }

 

 

3、獲取 Blog 專案使用者資料

 這個功能是一個充分不必要的條件,主要是為了解決某些專案先用了自身一套認證系統,然後又上了 IdentityServer4 專案的情況,就比如我們的 Blog.Core 專案,就是這個情況。

那使用者的資料從哪裡拿到呢,很巧,大家還記得我在某一篇文章《 42 ║支援多種資料庫 & 快速資料庫生成》中,自動生成資料庫的方案,就是這麼使用的,從 Github 上直接拉取資料即可,這裡整個派上用場,感覺很不錯,

 

private static string GitJsonFileFormat = "https://github.com/anjoy8/Blog.Data.Share/raw/master/Blog.Core.Data.json/{0}.tsv";


// 遠端獲取 Blog.Core 資料庫的三表資料
var BlogCore_Users = JsonHelper.ParseFormByJson<List<sysUserInfo>>(GetNetData.Get(string.Format(GitJsonFileFormat, "sysUserInfo")));
var BlogCore_Roles = JsonHelper.ParseFormByJson<List<Role>>(GetNetData.Get(string.Format(GitJsonFileFormat, "Role")));
var BlogCore_UserRoles = JsonHelper.ParseFormByJson<List<UserRole>>(GetNetData.Get(string.Format(GitJsonFileFormat, "UserRole")));

 

 

 

4、執行Seed方法,檢視資料

經歷了定義上下文 -> 資料庫遷移 -> 獲取資料 -> Seed Data 的過程後,就剩下最後一步了,執行操作:

很簡單,我在 Blog.Core 專案中,採用的是 appsettings.json 檔案配置的方法,這裡換另一種方法,Program.cs 控制檯引數的形式

 

 

四、美化部分頁面

 在上一講中,我們用到了官方的 快速啟動 程式碼,裡邊已經包含了基本的頁面,當然你也可以根據自己的情況去自定義,甚至採用靜態的Ajax 來實現,我這裡還是使用的 MVC 結構,只不過套用了下一個簡單樣式(樣式版權是http://www.uimaker.com/ 網站的,請不要做商業用途,注意產權),至少看起來好看了一些。

 

 

 

 具體的登入操作就不演示了,等下次把 增刪改查 都做好了,再統一做下動圖展示吧,具體如何設計,請聽下回分解。

 

五、結語

 今天我們簡單的實現了對 IdentityServer4-EFCore 的相關配置,概念不是很難,為了加深印象,還是提幾個小問題,請大家多思考:

1、Ids4 一共用到了幾個上下文,分別的用處是什麼?

2、在遷移中,資料庫生成了多少表,各個模組又是幹什麼的?

3、Ids4 的良好擴充套件性,體現在哪裡?豐富性又體現在哪裡?

4、ApplicationUser 類是起到什麼作用的?

5、思考1:如果同一個使用者有多個角色如何處理?

6、思考2:如果不使用 NetCore 自帶的 Identity 認證,如何自定義上下文?

 

 

六、Github

 

https://github.com/anjoy8/Blog.IdentityServer

 

---♥---♥--- ---♥---♥--- ---♥---♥---

相關文章