從壹開始 [ Ids4實戰 ] 之三║ 詳解受權持久化 & 用戶數據遷移

回顧

 哈嘍你們週三好,今天終於又從新開啓 IdentityServer4 的落地教程了,很少說,既然開始了,就要努力作好😸。javascript

書接上文,在好久以前的上篇文章《二║ 基礎知識集合 & 項目搭建一》中,咱們簡單的說了說 IdentityServer4 是如何調用和配置 Token 的,主要是一些入門的基礎知識和概念,也算是第一次嚐鮮了,其實 Ids4 自己沒有那麼神祕,咱們只須要知道它是一個豐富的認證框架,又具備很好的可擴展性,核心就是如何配置某些客戶端經過該受權服務來包括另外一些資源服務器,供用戶使用。由於中間隔了很長時間了,這裏簡單的回顧下上篇文章說了什麼,下邊這幾個小問題,相信你們腦中都能很快的有一些概念,至少是臉熟了,若是不是很清楚,請翻看上邊的連接,或者官網再看看:html

 

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

二、IdentityServer4 主要是受權仍是認證 ?java

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

四、Ids4 依賴的 Nuget 包是什麼 ?github

五、如何快速開發 UI ?sql

 

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

以前的配置都是在內存中,下面將如何把這些數據存儲到Sql Server數據庫, 這樣更符合生產環境的要求。好啦,立刻開始今天的內容~~~數據庫

 

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

 

1、基於 EFCore 的持久化操做

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

咱們要把全部的數據都存儲下來,採用接口進行建模,咱們在 IdentityServer4.EntityFramework Nuget 包中提供這些接口的 EF 實現。

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

 

   

二、三個上下文 —— 核心

這一節你們若是看懂的話,必定會對 IdentityServer4 豐富性和可擴展性 有特別切身的瞭解和體會!😁

 

Z、爲何要定義多個上下文?

爲何要多個上下文? 

還記得在第二個系列 DDD 中,咱們說到了多個上下文的用途,單個數據庫能夠有多個上下文。例如, 若是您的數據庫包含多個數據庫架構, 而且您但願將每一個架構做爲單獨的自包含區域處理, 則此功能很是有用。在使用多個上下文類型時,您還會看到其餘問題, 例如共享實體類型及其從一個上下文傳遞到另外一個上下文等。通常來講,設計多個上下文,會使您的設計更乾淨, 並分離不一樣的功能區域,但同時它的成本也增長了複雜性。框架之因此會設計成多個上下文,是爲了更好的擴展,咱們也能夠部署到不一樣的物理機數據庫中,更好的實現獨立性。

 

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

由於第三個上下文不是官方定義的,是咱們本身根據須要進行合理擴展的,好比咱們可使用 Asp.Net Core 自帶的 Identity 身份認證機制來實現擴展,固然,你也能夠本身定義相應的操做,若是你還不懂,就想象一下咱們在 Blog.Core 項目中,咱們不是本身定義的基於 JWT 的認證受權麼( 用戶表 + 角色表 + 關係表等等 ),可是若是你看個人 DDD 項目的話,應該能夠看到我又使用了 Core 官方自帶的 Identity 機制,畢竟結合 EFCore 咱們能夠很快的實現用戶數據的管理,而不用本身造輪子了。

 

 

我們這裏先說說前兩個上下文,在咱們使用 Ids4 的時候,有兩種類型的數據須要持久化到數據庫中:

一、配置數據(資源、客戶端、身份);//這裏是對應配置上下文 ConfigurationDbContext 

二、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);
     }
 }

這個上下文,主要是用來處理咱們的用戶數據相關的部分:

一、有多少用戶數據和角色數據;

二、哪些用戶,又擁有什麼角色,又對應哪些Claims 聲明。

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

咱們既然須要用到這幾個上下文,就須要添加他們所對應的服務,彆着急,往下看。 

 

 

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

 

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

 

因此,就須要在項目中配置對應的服務,咱們在 startup.cs 啓動文件中,配置服務 ConfigureService :

配置 EF 操做數據庫,很簡單,這個就按照平時咱們使用的正常的方法便可:

 

一、獲取數據庫鏈接字符串:

( 我這裏使用的是讀取文件的形式,其實就是字符串放到文件裏了,你們本身根據須要作相應調整便可,我這麼是防止密碼泄露。)

 

二、配置數據庫服務:

    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();

 

 

四、Migrations 遷移 —— 結果

我這裏提供了兩種方法,至於哪一種更好,你們能夠本身都試試,我都用過,可能第二種稍微多了一點,不過原理都是同樣的。

方式一:在包控制檯

 1、遷移項目( 使用 Package Manager Console ):
   一、add-migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb 二、add-migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb 三、add-migration AppDbMigration -c ApplicationDbContext -o Data 四、update-database -c PersistedGrantDbContext
五、update-database -c ConfigurationDbContext
六、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 了數據,那具體如何操做呢,別慌,第三節我會說到了,如今這個時候,咱們已經把數據庫的全部表都已經生成好了,先不着急同步數據,咱們先看看到底生成了哪些數據表。

 

 

 

2、數據庫表的含義(更新中)

 

一、總體數據模型圖

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

 

 接下來,我們針對這三個模塊簡單的說一說,由於這裏尚未數據,提及來可能會比較抽象,這裏先留一個坑,等配合 Vue 客戶端使用了之後,再把完整數據展現出來,可能更好的理解了。

 

二、三個模塊表

 

操做數據模塊

 

這是 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) ,光從數據庫表名上,咱們就知道其中的含義了,就是用戶角色管理。

下邊我們會將 Blog.Core 項目中的用戶信息導進來,先看看 AspNetUsers 表中是怎樣的數據:

 

 

 

上邊我們也簡單的說了下數據庫表結構,而後再結合三個上下文,你們應該都能明白各自的含義了,好啦,如今就是萬事俱備,只欠數據了,是時候動手了!

 

3、同步更新其餘項目的用戶數據


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

 

一、定義 SeedData 類

首先,須要定義一個 SeedData 類,主要是用來 update-database  和  Seed Data 這兩個做用,由於篇幅的緣由,具體的代碼在 Github 上,你們自行下載查看便可:

 

 

 

 

二、擴展用戶主表數據

 上邊我們也說到了,咱們經過繼承使用 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; }
  }

 

 

三、獲取 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")));

 

 

四、從新定義配置類 Config.cs  

 我們在上篇文章中,定義了一個 InMemoryConfig 基於內存的配置文件,我們再進行完善下,應用到真實項目 Blog.Vue 中:

    public class Config
    {
        // scopes define the resources in your system
        public static IEnumerable<IdentityResource> GetIdentityResources()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
                new IdentityResource("roles", "角色", new List<string> { JwtClaimTypes.Role }),
            };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource> {
                new ApiResource("blog.core.api", "Blog.Core API") {
                    // include the following using claims in access token (in addition to subject id)
                    //requires using using IdentityModel;
                    UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Role }
                }
            };
        }

        // clients want to access resources (aka scopes)
        public static IEnumerable<Client> GetClients()
        {
            // javascript client
            return new List<Client> {
                new Client {
                    ClientId = "blogvuejs",
                    ClientName = "Blog.Vue JavaScript Client",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    AllowAccessTokensViaBrowser = true,

                    RedirectUris =           { "http://localhost:6688/callback" },
                    PostLogoutRedirectUris = { "http://localhost:6688" },
                    AllowedCorsOrigins =     { "http://localhost:6688" },

                    AllowedScopes = {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "roles",
                        "blog.core.api"
                    }
                }
            };
        }
    }

 

 

五、執行Seed方法,查看數據

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

很簡單,我在 Blog.Core 項目中,採用的是 appsettings.json 文件配置的方法,這裏換另外一種方法,Program.cs 控制檯參數的形式

 

 

4、美化部分頁面

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

 

 

 

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

 

5、結語

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

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

二、在遷移中,數據庫生成了多少表,各個模塊又是幹什麼的?

三、Ids4 的良好擴展性,體如今哪裏?豐富性又體如今哪裏?

四、ApplicationUser 類是起到什麼做用的?

五、思考1:若是同一個用戶有多個角色如何處理?

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

 

 

6、Github

 

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

 

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

相關文章
相關標籤/搜索