使用Client Credentials模式控制API的訪問
本文基於官方文章翻譯和修改
客戶端憑證模式(Client Credentials)模式
客戶端憑證模式模式是IdentityServer4中控制API訪問的基本模式,我們通常通過定義一個API和訪問客戶端,客戶端通過客戶端ID請求Identity Server以獲取訪問的
token
本篇將通過創建一個用戶認證授權中心來保護API
編寫Identity server
Install-Package IdentityServer4 -Version 3.1.0
定義API Resource和客戶端
public static class Config
{
// Defining an API Resource
public static IEnumerable<ApiResource> Apis =>
new List<ApiResource>{
new ApiResource("CodeSnippets.WebApi","CodeSnippets API")
};
// Defining Client
public static IEnumerable<Client> Clients =>
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())
},
// scopes that client has access to
AllowedScopes={ "CodeSnippets.WebApi" }
}
};
}
配置IdentityServer
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddIdentityServer()
.AddInMemoryApiResources(Config.Apis)
.AddInMemoryClients(Config.Clients);
// builder.AddDeveloperSigningCredential();
}
配置完成後運行應用,訪問:http://localhost:5000/.well-known/openid-configuration將看到所謂的發現文檔(發現文檔是身份服務器中的標準端點,我們的客戶端和API將使用發現文檔來下載必要的配置數據)。
首次啓動時,IdentityServer將爲我們創建一個開發人員簽名密鑰,該文件名爲tempkey.rsa。我們無需將該文件簽入源代碼管理中,如果不存在該文件將被重新創建。
創建API
- 新建一個WebApi:
[Route("identity")]
[ApiController]
[Authorize]
public class IdentityController : ControllerBase
{
public IActionResult Get()
{
return new JsonResult(User.Claims.Select(c => new { c.Type, char.MinValue }));
}
}
配置launchSettings.json使其在端口5001運行
- 配置API
public void ConfigureServices(IServiceCollection services)
{
// 將身份驗證服務添加到DI並配置Bearer爲默認方案。
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", option =>
{
option.Authority = "http://localhost:5000";
option.RequireHttpsMetadata = false;
option.Audience = "CodeSnippets.WebApi";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication(); // 將身份驗證中間件添加到管道中,以便對主機的每次調用都將自動執行身份驗證。
app.UseAuthorization(); // 添加了授權中間件,以確保匿名客戶端無法訪問我們的API端點。
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute().RequireAuthorization();
//endpoints.MapHealthChecks("")
});
}
此時運行API,訪問http://localhost:5001/identity
返回401 Unauthorized
創建客戶端
客戶端請求訪問令牌,然後使用該令牌訪問API
。
IdentityServer上的令牌端點實現了OAuth 2.0協議,我們可以使用原始HTTP來訪問它。但是,我們有一個名爲IdentityModel的客戶端庫,該客戶端庫將協議交互封裝在易於使用的API中:
Install-Package IdentityModel -Version 4.1.1
-
步驟一 獲取被發現文檔
IdentityModel
包括一個與發現端點一起使用的客戶端庫。這樣,我們只需要知道IdentityServer的基地址-實際的端點地址就可以從元數據中讀取。 -
步驟二
使用發現文檔中的信息向IdentityServer請求令牌以訪問API
-
步驟三
調用API
static async Task Main(string[] args)
{
// 1.discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// 2.request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "CodeSnippets.WebApi"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("*".PadLeft(50, '*'));
// 3.call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("http://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
//Console.WriteLine(Newtonsoft.Json.Linq.JArray.Parse(content));
}
}
以上代碼執行結果: