IdentityServer4 是一個提供 認證服務,單點登錄/登出(SSO),API訪問控制,聯合認證通道的可定製、免費商業支持的框架。 ...
# IdentityServer4 中文文檔 -9- (快速入門)使用客戶端憑證保護API
原文:http://docs.identityserver.io/en/release/quickstarts/1_client_credentials.html
上一篇:IdentityServer4 中文文檔 -8- (快速入門)設置和概覽
下一篇:IdentityServer4 中文文檔 -10- (快速入門)使用密碼保護API
快速入門展示了使用 IdentityServer 保護 API 的最基礎的場景。
在這個場景中,我們定義一個 API,同時定義一個 想要訪問這個 API 的 客戶端。客戶端將從 IdentityServer 請求獲得一個訪問令牌,然後用這個令牌來獲得 API 的訪問許可權。
定義 API
範圍(Scopes)用來定義系統中你想要保護的資源,比如 API。
由於當前演練中我們使用的是記憶體配置 —— 添加一個 API,你需要做的只是創建一個 ApiResource
類型的實例,併為它設置合適的屬性。
向你的項目中添加一份文件(比如:Config.cs
),然後添加如下代碼:
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource("api1", "我的 API")
};
}
定義 客戶端
下一步是定義能夠訪問上述 API 的客戶端。
在該場景中,客戶端不會有用戶參與交互,並且將使用 IdentityServer 中所謂的客戶端密碼(Client Secret)來認證。添加如下代碼到你的配置中:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "client",
// 沒有交互性用戶,使用 clientid/secret 實現認證。
AllowedGrantTypes = GrantTypes.ClientCredentials,
// 用於認證的密碼
ClientSecrets =
{
new Secret("secret".Sha256())
},
// 客戶端有權訪問的範圍(Scopes)
AllowedScopes = { "api1" }
}
};
}
配置 IdentityServer
為了讓 IdentityServer 使用你的 Scopes 和 客戶端 定義,你需要向 ConfigureServices
方法中添加一些代碼。你可以使用便捷的擴展方法來實現 —— 它們在幕後會添加相關的存儲和數據到 DI 系統中:
public void ConfigureServices(IServiceCollection services)
{
// 使用記憶體存儲,密鑰,客戶端和資源來配置身份伺服器。
services.AddIdentityServer()
.AddTemporarySigningCredential()
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
現在,如果你運行伺服器並將瀏覽器導航到 http://localhost:5000/.well-known/openid-configuration
,你應該看能到所謂的 發現文檔。你的客戶端和 API 將使用這些信息來下載所需要的配置數據。
添加 API
下一步,向你的解決方案中添加 API。
你可以使用 ASP.NET Core Web API 模板,或者添加 Microsoft.AspNetCore.Mvc
程式包到你的項目中。再一次建議你像之前一樣,控制所使用的埠並使用與之前配置 Kestrel 和啟動資料相同的技術。該演練假設你已經將你的 API 配置為運行於 http://localhost:5001
之上。
控制器
添加一個新的控制器到你的 API 項目中:
[Route("identity")]
[Authorize]
public class IdentityController : ControllerBase
{
[HttpGet()]
public IActionResult Get()
{
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
}
}
這個控制器將在後面被用於測試授權需求,同時通過API的眼睛(瀏覽工具)來可視化身份信息。
配置
接下來,添加認證中間件到 API 宿主。該中間件的主要工作是:
- 驗證輸入的令牌以確保它來自可信任的發佈者(IdentityServer);
- 驗證令牌是否可用於該 api(也就是 Scope)。
將 IdentityServer4.AccessTokenValidation
NuGet 程式包添加到你的 API 項目:
你還需要添加中間件到你的 HTTP 管道中 —— 必須在添加 MVC 之前添加,比如:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions()
{
Authority = "http://localhost:5000",
RequireHttpsMetadata = false,
ApiName = "api1"
});
app.UseMvc();
}
如果你使用瀏覽器導航到上述控制器(http://localhost:5001/identity),你應該收到返回的 401
狀態碼。這意味著你的 API 要求提供證書。
這樣一來, API 就是受 IdentityServer 保護的了。
創建客戶端
最後一個步驟是編寫一個客戶端來請求訪問令牌,然後使用這個令牌來訪問 API。為此你需要為你的解決方案添加一個控制台應用程式。
IdentityServer 上的令牌端點實現了 OAuth 2.0
協議,你應該使用合法的 HTTP 來訪問它。然而,我們有一個叫做 IdentityModel
的客戶端庫,它將協議交互封裝到了一個易於使用的 API 裡面。
添加 IdentityModel
NuGet 程式包到你的客戶端項目中。
IdentityModel 包含了一個用於 發現端點 的客戶端庫。這樣一來你只需要知道 IdentityServer 的基礎地址 —— 實際的端點地址可以從元數據中讀取:
// 從元數據中發現埠
var disco = await DiscoveryClient.GetAsync("http://localhost:5000");
接著你可以使用 TokenClient
類型來請求令牌。為了創建一個該類型的實例,你需要傳入令牌端點地址、客戶端id和密碼。
然後你可以使用 RequestClientCredentialsAsync
方法來為你的目標 API 請求一個令牌:
// 請求以獲得令牌
var tokenClient = new TokenClient(disco.TokenEndpoint, "client", "secret");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("api1");
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
註意:從控制臺中複製和粘貼訪問令牌到 jwt.io 以檢查令牌的合法性。
最後是調用 API。
為了發送訪問令牌到 API,你一般要使用 HTTP 授權 header。這可以通過 SetBearerToken
擴展方法來實現:
// 調用API
var client = new HttpClient();
client.SetBearerToken(tokenResponse.AccessToken);
var response = await client.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
最終輸出看起來應該是這樣的:
註意:預設情況下訪問令牌將包含 scope 身份信息,生命周期(nbf 和 exp),客戶端 ID(client_id) 和 發行者名稱(iss)。
進一步實踐
當前演練目前主要關註的是成功的步驟:
- 客戶端可以請求令牌
- 客戶端可以使用令牌來訪問 API
你現在可以嘗試引發一些錯誤來學習系統的相關行為,比如:
- 嘗試在 IdentityServer 未運行時(unavailable)連接它
- 嘗試使用一個非法的客戶端id或密碼來請求令牌
- 嘗試在請求令牌的過程中請求一個非法的 scope
- 嘗試在 API 未運行時(unavailable)調用它
- 不向 API 發送令牌
- 配置 API 為需要不同於令牌中的 scope
上一篇:IdentityServer4 中文文檔 -8- (快速入門)設置和概覽
下一篇:IdentityServer4 中文文檔 -10- (快速入門)使用密碼保護API