一. 概述 本篇開始進入IS4實戰學習,從第一個示例開始,該示例是 “使用客戶端憑據保護API”,這是使用IdentityServer保護api的最基本場景。該示例涉及到三個項目包括:IdentityServer項目、API項目、Client項目,都有自己的宿主,為了方便開發,放在了一個解決方案下( ...
一. 概述
本篇開始進入IS4實戰學習,從第一個示例開始,該示例是 “使用客戶端憑據保護API”,這是使用IdentityServer保護api的最基本場景。該示例涉及到三個項目包括:IdentityServer項目、API項目、Client項目,都有自己的宿主,為了方便開發,放在了一個解決方案下(Quickstart.sln),三個項目的分工如下:
(1) IdentityServer項目是包含基本的IdentityServer設置的ASP.NET Core應用程式,是令牌端點。
(2) API項目是Web Api,是要保護的資源。
(3) Client項目是客戶端用戶,用來訪問Web Api。
最後客戶端Client項目請求獲取IdentityServer上的訪問令牌。作為客戶端Client和IdentityServer都知道secret密鑰,Client將使用令牌訪問Web API。開源地址Github
二. 創建IdentityServer項目
創建一個ASP.NET Core Web(或空)模板。項目名為IdentityServer,解決方案為Quickstart。是一個包含基本IdentityServer設置的ASP.NET Core應用程式。該項目使用的協議是http,當在Kestrel上運行時,埠設置為5000或在IISExpress上的隨機埠。
首次啟動時,IdentityServer將為您創建一個開發人員簽名密鑰,它是一個名為的文件tempkey.rsa。您不必將該文件檢入源代碼管理中,如果該文件不存在,將重新創建該文件。項目最終目錄結構如下所示:
下麵進行說明,以及用序號來表示開發實現步驟:
2.1 安裝:Install-Package IdentityServer4
2.2 新增Config.cs文件, 該文件是IdentityServer資源和客戶端配置文件。在該文件中定義API資源,以及定義客戶端(可以訪問此API的客戶端)
/// <summary> /// 定義API資源,要保護的資源 /// </summary> /// <returns></returns> public static IEnumerable<ApiResource> GetApis() { return new List<ApiResource> { new ApiResource("api1", "My API") }; }
/// <summary> /// 定義客戶端,可以訪問此API的客戶端 /// </summary> /// <returns></returns> public static IEnumerable<Client> GetClients() { return new List<Client> { new Client { ClientId = "client", // no interactive user, use the clientid/secret for authentication AllowedGrantTypes = GrantTypes.ClientCredentials, //使用密鑰進行身份認證 secret for authentication ClientSecrets = { new Secret("secret".Sha256()) }, //客戶端允許訪問的範圍 AllowedScopes = { "api1" } } }; }
2.3 Startup配置
/// <summary> /// 配置IdentityServer,載入API資源和客戶端 /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { // uncomment, if you wan to add an MVC-based UI //services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1); //添加AddIdentityServer var builder = services.AddIdentityServer() //添加記憶體的Identity資源 .AddInMemoryIdentityResources(Config.GetIdentityResources()) //添加api資源 .AddInMemoryApiResources(Config.GetApis()) //添加clinet .AddInMemoryClients(Config.GetClients()); if (Environment.IsDevelopment()) {
//開發環境下使用臨時簽名憑據 builder.AddDeveloperSigningCredential(); } else { throw new Exception("need to configure key material"); } }
public void Configure(IApplicationBuilder app) { if (Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // uncomment if you want to support static files //app.UseStaticFiles(); app.UseIdentityServer(); // uncomment, if you wan to add an MVC-based UI //app.UseMvcWithDefaultRoute(); }
運行伺服器並瀏覽瀏覽器 http://localhost:5000/.well-known/openid-configuration, 客戶端和API將使用它來下載必要的配置數據。下麵是截取的部分配置數據:
三. 創建API項目
在解決方案下繼續添加API項目,添加ASP.NET Core Web API(或空)模板。將API應用程式配置為http://localhost:5001運行。項目最終目錄結構如下所示:
(1) 在API項目中添加一個新文件夾Controllers和一個新控制器IdentityController
//定義路由 [Route("identity")] //需要授權 [Authorize] public class IdentityController : ControllerBase { /// <summary> /// 測試授權,獲取該用戶下聲明集合Claims /// </summary> /// <returns></returns> public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } }
(2) Startup配置
public void ConfigureServices(IServiceCollection services) { //將最基本的MVC服務添加到服務集合中 services.AddMvcCore() //向基本的MVC服務中添加授權 .AddAuthorization() //向基本的MVC服務中添加格式化 .AddJsonFormatters(); //將身份驗證服務添加到DI服務集合中,並配置"Bearer"為預設方案 services.AddAuthentication("Bearer") //驗證令牌是否有效用於此API .AddJwtBearer("Bearer", options => { options.Authority = "http://localhost:5000"; //在開發環境禁用,預設true options.RequireHttpsMetadata = false; options.Audience = "api1"; }); }
public void Configure(IApplicationBuilder app) { //添加身份驗證中間件 app.UseAuthentication(); app.UseMvc(); }
啟動程式運行http://localhost:5001/identity時返回401狀態碼,未授權。意味著API需要憑證,現在受IdentityServer保護。如下所示:
四.創建Client項目
我們通過上面知道,直接用瀏覽器來訪問API是返回401狀態碼未授權,下麵在Client項目中使用憑證,來獲得api授權訪問。下麵是Client項目目錄結構,這裡Client是一個控制台應用程式。對於客戶端可以是任意應用程式,比如手機端,web端,win服務等等。
在IdentityServer的令牌端點實現了OAuth 2.0協議,客戶端可以使用原始HTTP來訪問它。但是,我們有一個名為IdentityModel的客戶端庫,它將協議交互封裝在易於使用的API中。
3.1 安裝:Install-Package IdentityModel
3.2 發現IdentityServer端點
IdentityModel包括用於發現端點的客戶端庫。只需要知道IdentityServer的基地址 - 可以從元數據中讀取實際的端點地址:
private static async Task Main() { // discover endpoints from metadata var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000"); if (disco.IsError) { //當停掉IdentityServer服務時 //Error connecting to http://localhost:5000/.well-known/openid-configuration: 由於目標電腦積極拒絕,無法連接。 Console.WriteLine(disco.Error); return; } //...
其中GetDiscoveryDocumentAsync是屬於IdentityModel庫的,是對HttpClient擴展方法。http://localhost:5000是IdentityServer的基地址。
3.3 請求令牌Token
在Mian方法中繼續向IdentityServer請求令牌,訪問api1資源。這裡的RequestClientCredentialsTokenAsync方法也是HttpClient擴展方法。
// request token,帶入需要的4個參數,請求令牌,返回TokenResponse var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { //IdentityServer基地址 http://localhost:5000/connect/token Address = disco.TokenEndpoint, //設置客戶端標識 ClientId = "client", //設置密鑰 ClientSecret = "secret", //訪問的資源範圍 Scope = "api1" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } //列印 token 信息 Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n");
3.4 調用API
在Mian方法中繼續向下,當訪問令牌取得後,開始調用Web API。 下麵將訪問令牌發送到Web API,通常使用HTTP Authorization標頭。這是使用SetBearerToken擴展方法完成的,該方法是IdentityModel庫的HttpClient擴展方法。
// call api var apiClient = new HttpClient(); //發送訪問令牌 apiClient.SetBearerToken(tokenResponse.AccessToken); //訪問API,獲取該用戶下聲明集合Claims var response = await apiClient.GetAsync("http://localhost:5001/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { //輸出 claims 名稱值 對 var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); }
下麵開始測試,先啟動IdentityServer程式,再啟動API程式,最後啟動Client客戶端來訪問API,通過下圖可以瞭解到:(1)客戶端請求令牌成功,(2) 客戶端使用令牌來訪問API成功。
如果想進一步嘗試激發錯誤,來瞭解系統的行為,可以錯誤的去配置如下:
(1) 嘗試停掉IdentityServer服務程式,這個已經測試了。
(2) 嘗試使用無效的客戶端ID標識 ClientId = "client",
(3) 嘗試在令牌請求期間請求無效範圍 Scope = "api1"
(4) 嘗試在API程式未運行時調用API
(5) 嘗試不要將令牌發送到API
總結:通過本篇瞭解到了IS4保護api的最基本場景。流程是首先創建一個IdentityServer 令牌程式。 接著創建API項目,使用IdentityServer令牌程式來保護API。 最後創建要訪問的Client項目,獲取訪問令牌後再調用API方法。
IdentityServer令牌端對要保護API資源做了配置 new ApiResource("api1", "My API")
限制了訪問Api的客戶端標識和訪問資源範圍ClientId = "client", AllowedScopes = { "api1" }還有客戶端需要的秘鑰。
參考文獻