【One by One系列】IdentityServer4(八)使用EntityFramework Core對資料進行持久化
上幾篇,我們建立了客戶端,scope
,啟動時,IdentityServer
把這些配置資料載入至記憶體,但是,如果我們想要更改配置,就必須停掉IdentityServer,然後重新啟動。且IdentityServe在r執行過程中還會生成臨時資料,如授權碼、是否同意的按鈕選擇、以及refresh token。預設情況下,這些也儲存在記憶體中。
將以上這些資料儲存在資料庫中進行資料持久化,方便重啟跨多個IdentityServer例項,這個持久化,我們可以使用IdentityServer4 Entity Framework
除了手動配置EF支援之外,還有一個IdentityServer模板可以使用,dotnet new is4ef建立一個支援EF的新專案。
IdentityServer4.EntityFramework
nuget包實現了所需的儲存和服務,主要使用以下兩個DbContexts:
ConfigurationDbContext
- 作用於註冊資料,如客戶端,資源,scope等等PersistedGrantDbContext
- 作用於臨時操作資料,如授權碼,refresh tokens
這些context適用於任何ef core相容的關係型資料庫,sqlserver,mysql。
可以在
-
IdentityServer4.EntityFramework.Storage
包中找到context
,entities
,IdentityServer4 stores
-
IdentityServer4.EntityFramework
包括了註冊的擴充套件方法,且包括了IdentityServer4.EntityFramework.Storage
1.新增nuget引用
cd .\IdentityServer\
dotnet add package IdentityServer4.EntityFramework
2.新增對mysql的支援
dotnet add package MySql.Data.EntityFrameworkCore
3.資料遷移
IdentityServer4.EntityFramework.Storage包存在包含對映自IdentityServer模型的實體類,隨著IdentityServer的模型的更改,IdentityServer4.EntityFramework.Storage中的實體類也將更改,所以需要使用者隨著時間的推移,升級使用這個包,這個過程,需要負責在資料庫架構以及在實體類更改時,對該資料庫架構進行必要的更改。最好的方式就是使用EF
這裡官方只提供了針對sqlserver的sql指令碼,可以看一下,做個瞭解。
4.重新配置儲存
在Startup.cs
using Microsoft.EntityFrameworkCore;
using System.Reflection; //這裡用到了反射
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
//3308為宿主機埠,對映docker mysql容器預設埠3306
const string connectionString = @"Persist Security Info=False;database=IdentityServer4;server=localhost;port=3308;Connect Timeout=30;user id=root; pwd=123456";
services.AddIdentityServer()
.AddTestUsers(TestUsers.Users)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b => b.UseMySQL(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b => b.UseMySQL(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
});
因為我們在IdentityServer.csproj中使用EF遷移,所以通過對MigrationsAssembly
的呼叫來告訴Entity Framework 的宿主專案(IdentityServer.csproj)將包含遷移程式碼(the migrations code)。這是必要的,因為宿主專案(IdentityServer.csproj)與包含DbContext類的專案,兩者是位於不同的程式集中(IdentityServer4.EntityFramework.Storage)。
5.建立遷移
一旦將IdentityServer配置為使用 Entity Framework Core,我們將需要生成一些遷移-migrations。
Entity Framework Core CLI
Microsoft.EntityFrameworkCore.Design
nuget包
#安裝ef core 工具
dotnet tool install --global dotnet-ef
dotnet add package Microsoft.EntityFrameworkCore.Design
#cd到IdentityServer專案目錄
dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb
溫故而知新:還記得在VS的Package Manager Console是如何執行命令建立遷移的嗎?
#第一步 Add-Migration InitialCreate #第二步 Update-Database
6.初始化資料庫
現在我們已經完成了遷移,我們可以編寫程式碼從遷移-migrations建立資料庫。我們還可以使用在前面的quickstart中定義的記憶體配置資料來為資料庫初始化種子,當然這個seed最好只是在除錯環境下執行。
官方提示:在這個快速入門中使用的方法主要是使IdentityServer更容易啟動和執行。您應該設計適合自己體系結構的資料庫建立和維護策略。
在Startup.cs中增加下面的初始化方法:
using System.Linq;
using IdentityServer4.EntityFramework.DbContexts;
using IdentityServer4.EntityFramework.Mappers;
private void InitializeDatabase(IApplicationBuilder app)
{
using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
context.Database.Migrate();
if (!context.Clients.Any())
{
foreach (var client in Config.Clients)
{
context.Clients.Add(client.ToEntity());
}
context.SaveChanges();
}
if (!context.IdentityResources.Any())
{
foreach (var resource in Config.IdentityResources)
{
context.IdentityResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
if (!context.ApiResources.Any())
{
foreach (var resource in Config.Apis)
{
context.ApiResources.Add(resource.ToEntity());
}
context.SaveChanges();
}
}
}
public void Configure(IApplicationBuilder app)
{
// this will do the initial DB population
InitializeDatabase(app);
// the rest of the code that was already here
// ...
}
上面的InitializeDatabase方法可以方便地 seed the database,但是這種方法在每次執行應用程式時都留進去執行並不理想。一旦填充資料庫初始化資料之後,就可以考慮刪除對其之呼叫。
7.執行客戶端應用
這個就簡略些,上個命令即可
cd src\IdentityServer
dotnet run