Tip: 此篇已加入 .NET Core微服務基礎系列文章索引
一、IdentityServer的預備知識
要學習IdentityServer,事先得了解一下基于Token的驗證體系,這是一個龐大的主題,涉及到Token,OAuth&OpenID,JWT,協定規範等等等等,園子裡已經有很多介紹的文章了,個人覺得solenovex的這一篇文章《
學習IdentityServer4的預備知識》言簡意赅,可以快速的看看。另外savaboard的《
ASP.NET Core 之 Identity 入門(一)》和《
ASP.NET Core 之 Identity 入門(二)》這兩篇也可以一看,對Claims和Identity的基本知識講的比較通俗易懂,深入淺出,有故事情節,哈哈。
重點關注一下上面這張圖(也是來自solenovex的文章),對于一個User(已注冊)來說,他會首先向Authorization Server表明自己的身份(比如輸入使用者名和密碼),然後Authorization Server為其發放了一個token,而這個token就好比是把家裡的鑰匙配了一把(clone)新的,此後該User就可以通路API請求擷取Orders(訂單)資料了。當然,實際中可能Authorization Server和API Server不在同一個區域内,它們可能隻能遙望對方。此外,User還可以基于這個token去通路第三方服務,第三方服務會使用這個API來通路API Server,向其提供token比提供username&password要安全得多。
二、IdentityServer極簡介紹
IdentityServer4(這裡隻使用版本号為4)是一個基于OpenID Connect和OAuth 2.0的針對ASP.NET Core 2.0的架構。IdentityServer是将規範相容的OpenID Connect和OAuth 2.0終結點添加到任意ASP.NET Core應用程式的中間件。通常,你建構(或重新使用)包含登入和登出頁面的應用程式,IdentityServer中間件會向其添加必要的協定頭,以便用戶端應用程式可以使用這些标準協定與其對話。
我們可以用IdentityServer來做啥?
(1)身份驗證服務=>官方認證的OpenID Connect實作
(2)單點登入/登出(SSO)
(3)通路受控的API=>為不同的客戶提供通路API的令牌,比如:MVC網站、SPA、Mobile App等
(4)等等等......
三、Started:第一個AuthorizationServer
1.1 建立一個ASP.NET Core空Web項目
建立ASP.NET Core項目,使用Empty空模闆。
為了更好地檢視日志資訊,同時考慮到IISExpress啟動起來真的很慢,修改lanuchSettings.json檔案如下:
{
"profiles": {
"Manulife.DNC.MSAD.IdentityServer4Test": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5000/"
}
}
}
1.2 安裝并配置IdentityServer4
Step1.首先安裝IdentityServer4:
NuGet>Install-Package IdentityServer4
Step2.配置ASP.NET Core管道,即修改Configure方法
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
}
Step3.為了要把IdentityServer注冊到容器中,需要對其進行配置,而這個配置中要包含三個資訊:
(1)哪些API可以使用這個AuthorizationServer
(2)哪些Client可以使用這個AuthorizationServer
(3)哪些User可以被這個AuthrizationServer識别并授權
這裡為了快速示範,我們寫一個基于記憶體的靜态類來快速填充上面這些資訊(實際中,可以持久化在資料庫中通過EF等ORM擷取,也可以通過Redis擷取):
public class InMemoryConfiguration
{
public static IConfiguration Configuration { get; set; }
/// <summary>
/// Define which APIs will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new[]
{
new ApiResource("clientservice", "CAS Client Service"),
new ApiResource("productservice", "CAS Product Service"),
new ApiResource("agentservice", "CAS Agent Service")
};
}
/// <summary>
/// Define which Apps will use thie IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<Client> GetClients()
{
return new[]
{
new Client
{
ClientId = "client.api.service",
ClientSecrets = new [] { new Secret("clientsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice" }
},
new Client
{
ClientId = "product.api.service",
ClientSecrets = new [] { new Secret("productsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "clientservice", "productservice" }
},
new Client
{
ClientId = "agent.api.service",
ClientSecrets = new [] { new Secret("agentsecret".Sha256()) },
AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials,
AllowedScopes = new [] { "agentservice", "clientservice", "productservice" }
}
};
}
/// <summary>
/// Define which uses will use this IdentityServer
/// </summary>
/// <returns></returns>
public static IEnumerable<TestUser> GetUsers()
{
return new[]
{
new TestUser
{
SubjectId = "10001",
Username = "[email protected]",
Password = "edisonpassword"
},
new TestUser
{
SubjectId = "10002",
Username = "[email protected]",
Password = "andypassword"
},
new TestUser
{
SubjectId = "10003",
Username = "[email protected]",
Password = "leopassword"
}
};
}
}
Step4.對于Token簽名需要一對公鑰和私鑰,不過IdentityServer為開發者提供了一個_AddDeveloperSigningCredential()_方法,它會幫我們搞定這個事,并預設存到硬碟中。當切換到生産環境時,還是得使用正兒八經的證書,更換為使用_AddSigningCredential()_方法。
public void ConfigureServices(IServiceCollection services)
{
InMemoryConfiguration.Configuration = this.Configuration;
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
}
1.3 擷取你心心念念的Token
Step1.啟動剛剛我們建立的AuthorizationServer程式,這裡我們綁定的是5000端口。
Step2.啟動Postman/SoapUI等API測試工具,通過向HTTP Body中填寫資料發起POST請求:
Step3.發送一個錯誤的資料,看看傳回的是啥?(這裡輸入了一個不在定義清單中的client_id)
Step4.檢視控制台的日志資訊:表示擷取Token的這個請求成功了,日志中client_secret和password都是不會直接明文顯示的。
Step5.IdentityServer中我們設定這幾個API Service的Grant_Type是ResourceOwnerPasswordAndClientCredentials(點選這裡了解=>
資源擁有者密碼憑據許可),是以我們還可以使用ClientCredentials(點選這裡了解=>
用戶端憑據許可),如下所示:
Step6.再次檢視控制台日志資訊:這次沒有關于User相關的任何資訊顯示了。
Step7.基本的開發結束,對于開發階段,我們使用IdentityServer為開發者提供的臨時證書即可,但是後面仍然需要生成一些正兒八經的證書。這裡我們通過OpenSSL來生成,首先去
官網下載下傳一個,這裡使用的是Win64_1.1版本。打開Powershell或者CMD,輸入以下指令:
cmd>openssl req -newkey rsa:2048 -nodes -keyout cas.clientservice.key -x509 -days 365 -out cas.clientservice.cer
下面将生成的證書和Key封裝成一個檔案,以便IdentityServer可以使用它們去正确地簽名tokens
cmd>openssl pkcs12 -export -in cas.clientservice.cer -inkey cas.clientservice.key -out cas.clientservice.pfx
中途會提示讓你輸入Export Password,這個password後面會用到,記住它。最終導出後的結果如下圖所示:
這裡我将其放到了項目結構檔案夾中,并設定這個pfx檔案為“如果較新則複制”,確定可以在最後生成的目錄裡邊。現在就可以修改一下ConfigureServices()方法了:
public void ConfigureServices(IServiceCollection services)
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
InMemoryConfiguration.Configuration = this.Configuration;
services.AddIdentityServer()
//.AddDeveloperSigningCredential()
.AddSigningCredential(new X509Certificate2(Path.Combine(basePath,
Configuration["Certificates:CerPath"]),
Configuration["Certificates:Password"]))
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
}
這裡我将證書的路徑和導出密碼都寫到了配置檔案中:
{
"Certificates": {
"CerPath": "certificate\\cas.clientservice.pfx",
"Password": "manulife"
}
}
好,配置正兒八經的證書這一步驟Over。
四、IdentityServer QuickStart-UI
4.1 關于QuickStart UI
IdentityServer為我們提供了一套UI以便使我們能夠快速地開發具有基本功能的認證/授權界面,我們可以去這個位址:
https://github.com/IdentityServer/IdentityServer4.Quickstart.UI/tree/release下載下傳,并将其複制到我們的項目目錄中。
複制完成後,我們的項目結構如下圖所示:
4.2 修改DI方法
(1)使用MVC與靜态檔案(由于wwwroot下有很多靜态資源檔案)
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
// for QuickStart-UI
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
(2)注冊MVC
public void ConfigureServices(IServiceCollection services)
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
InMemoryConfiguration.Configuration = this.Configuration;
services.AddIdentityServer()
//.AddDeveloperSigningCredential()
.AddSigningCredential(new X509Certificate2(Path.Combine(basePath,
Configuration["Certificates:CerPath"]),
Configuration["Certificates:Password"]))
.AddTestUsers(InMemoryConfiguration.GetUsers().ToList())
.AddInMemoryClients(InMemoryConfiguration.GetClients())
.AddInMemoryApiResources(InMemoryConfiguration.GetApiResources());
// for QuickStart-UI
services.AddMvc();
}
4.3 Run
(1)首頁(這裡由于我已經登入,是以這裡會把我的賬号顯示了出來)
(2)Logout頁,剛剛說到我已經實作Login了,是以我這裡Logout一下
(3)Login頁:這裡隻能識别我們在之前配置的靜态User清單中那些User
登入之後,顯示:"You have not given access to any applications",表示我們還沒有給他授予通路任何API或網站子產品的權限。後續我們會建立API和MVC網站來示範如何對其進行授權和通路。
五、小結
本篇主要簡單的介紹了IdentityServer以及如何基于IdentityServer建立一個基本的AuthorizationServer,如何擷取Token,以及內建QuickStart UI實作基本的界面展示。後續還會建立API和MVC網站,來和IdentityServer進行內建,以示範如何對User授予通路API和MVC網站的通路權限。
示例代碼
Click =>
https://github.com/EdisonChou/EDC.IdentityServer4.Demo參考資料
《
identityserver4官方文檔》_=> 重點關注那些流程圖與術語_
ddrsql,《
IdentityServer4之Resource Owner Password Credentials(資源擁有者密碼憑據許可)》
IdentityServer4之Client Credentials(用戶端憑據許可)solenovex,《
學習Identity Server4的預備知識 使用Identity Server 4建立Authorization Server (1) 使用Identity Server 4建立Authorization Server (2) 使用Identity Server 4建立Authorization Server (3)