一. 概述
本篇開始進入IS4實戰(zhàn)學(xué)習(xí),從第一個示例開始慧瘤,該示例是 “使用客戶端憑據(jù)保護API”戴已,這是使用IdentityServer保護api的最基本場景。該示例涉及到三個項目包括:IdentityServer項目锅减、API項目糖儡、Client項目,都有自己的宿主怔匣,為了方便開發(fā)握联,放在了一個解決方案下(Quickstart.sln),三個項目的分工如下:
(1) IdentityServer項目是包含基本的IdentityServer設(shè)置的ASP.NET Core應(yīng)用程序,是令牌端點金闽。
(2) API項目是Web Api纯露,是要保護的資源。
(3) Client項目是客戶端用戶代芜,用來訪問Web Api埠褪。
二. 創(chuàng)建IdentityServer項目
創(chuàng)建一個ASP.NET Core Web(或空)模板。項目名為IdentityServer,解決方案為Quickstart蜒犯。是一個包含基本IdentityServer設(shè)置的ASP.NET Core應(yīng)用程序组橄。該項目使用的協(xié)議是http,當(dāng)在Kestrel上運行時罚随,端口設(shè)置為5000或在IISExpress上的隨機端口玉工。
首次啟動時,IdentityServer將為您創(chuàng)建一個開發(fā)人員簽名密鑰淘菩,它是一個名為的文件tempkey.rsa遵班。您不必將該文件檢入源代碼管理中,如果該文件不存在潮改,將重新創(chuàng)建該文件狭郑。項目最終目錄結(jié)構(gòu)如下所示:
下面進行說明,以及用序號來表示開發(fā)實現(xiàn)步驟:
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()
//添加內(nèi)存的Identity資源
.AddInMemoryIdentityResources(Config.GetIdentityResources())
//添加api資源
.AddInMemoryApiResources(Config.GetApis())
//添加clinet
.AddInMemoryClients(Config.GetClients());
if (Environment.IsDevelopment())
{
//開發(fā)環(huán)境下使用臨時簽名憑據(jù)
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();
}
運行服務(wù)器并瀏覽瀏覽器 http://localhost:5000/.well-known/openid-configuration, 客戶端和API將使用它來下載必要的配置數(shù)據(jù)亩鬼。下面是截取的部分配置數(shù)據(jù):
三. 創(chuàng)建API項目
在解決方案下繼續(xù)添加API項目,添加ASP.NET Core Web API(或空)模板阿蝶。將API應(yīng)用程序配置為http://localhost:5001運行雳锋。項目最終目錄結(jié)構(gòu)如下所示:
(1) 在API項目中添加一個新文件夾Controllers和一個新控制器IdentityController
//定義路由
[Route("identity")]
//需要授權(quán)
[Authorize]
public class IdentityController : ControllerBase
{
/// <summary>
/// 測試授權(quán)羡洁,獲取該用戶下聲明集合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服務(wù)添加到服務(wù)集合中
services.AddMvcCore()
//向基本的MVC服務(wù)中添加授權(quán)
.AddAuthorization()
//向基本的MVC服務(wù)中添加格式化
.AddJsonFormatters();
//將身份驗證服務(wù)添加到DI服務(wù)集合中辛蚊,并配置"Bearer"為默認方案
services.AddAuthentication("Bearer")
//驗證令牌是否有效用于此API
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:5000";
//在開發(fā)環(huán)境禁用,默認true
options.RequireHttpsMetadata = false;
//訂閱者資源范圍
options.Audience = "api1";
});
}
public void Configure(IApplicationBuilder app)
{
//添加身份驗證中間件
app.UseAuthentication();
app.UseMvc();
}
啟動程序運行http://localhost:5001/identity時返回401狀態(tài)碼,未授權(quán)卧檐。意味著API需要憑證霉囚,現(xiàn)在受IdentityServer保護盈罐。如下所示:
四.創(chuàng)建Client項目
我們通過上面知道盅粪,直接用瀏覽器來訪問API是返回401狀態(tài)碼未授權(quán)票顾,下面在Client項目中使用憑證,來獲得api授權(quán)訪問含鳞。下面是Client項目目錄結(jié)構(gòu)蝉绷,這里Client是一個控制臺應(yīng)用程序。對于客戶端可以是任意應(yīng)用程序磁滚,比如手機端,web端晒他,win服務(wù)等等津滞。
在IdentityServer的令牌端點實現(xiàn)了OAuth 2.0協(xié)議,客戶端可以使用原始HTTP來訪問它撞鹉。但是,我們有一個名為IdentityModel的客戶端庫颖侄,它將協(xié)議交互封裝在易于使用的API中鸟雏。
3.1 安裝:Install-Package IdentityModel
3.2 發(fā)現(xiàn)IdentityServer端點
IdentityModel包括用于發(fā)現(xiàn)端點的客戶端庫。只需要知道IdentityServer的基地址 - 可以從元數(shù)據(jù)中讀取實際的端點地址:
private static async Task Main()
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:5000");
if (disco.IsError)
{
//當(dāng)停掉IdentityServer服務(wù)時
//Error connecting to http://localhost:5000/.well-known/openid-configuration: 由于目標(biāo)計算機積極拒絕览祖,無法連接孝鹊。
Console.WriteLine(disco.Error);
return;
}
//...
其中GetDiscoveryDocumentAsync是屬于IdentityModel庫的,是對HttpClient擴展方法穴墅。http://localhost:5000是IdentityServer的基地址惶室。
3.3 請求令牌Token
在Mian方法中繼續(xù)向IdentityServer請求令牌,訪問api1資源玄货。這里的RequestClientCredentialsTokenAsync方法也是HttpClient擴展方法皇钞。
// request token,帶入需要的4個參數(shù)松捉,請求令牌夹界,返回TokenResponse
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
//IdentityServer基地址 http://localhost:5000/connect/token
Address = disco.TokenEndpoint,
//設(shè)置客戶端標(biāo)識
ClientId = "client",
//設(shè)置密鑰
ClientSecret = "secret",
//訪問的資源范圍
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
//打印 token 信息
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
3.4 調(diào)用API
在Mian方法中繼續(xù)向下,當(dāng)訪問令牌取得后隘世,開始調(diào)用Web API可柿。 下面將訪問令牌發(fā)送到Web API,通常使用HTTP Authorization標(biāo)頭丙者。這是使用SetBearerToken擴展方法完成的复斥,該方法是IdentityModel庫的HttpClient擴展方法。
// call api
var apiClient = new HttpClient();
//發(fā)送訪問令牌
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成功。
如果想進一步嘗試激發(fā)錯誤主儡,來了解系統(tǒng)的行為奖唯,可以錯誤的去配置如下:
(1) 嘗試停掉IdentityServer服務(wù)程序,這個已經(jīng)測試了糜值。
(2) 嘗試使用無效的客戶端ID標(biāo)識 ClientId = "client",
(3) 嘗試在令牌請求期間請求無效范圍 Scope = "api1"
(4) 嘗試在API程序未運行時調(diào)用API
(5) 嘗試不要將令牌發(fā)送到API
總結(jié):通過本篇了解到了IS4保護api的最基本場景丰捷。流程是首先創(chuàng)建一個IdentityServer 令牌程序坯墨。 接著創(chuàng)建API項目,使用IdentityServer令牌程序來保護API瓢阴。 最后創(chuàng)建要訪問的Client項目畅蹂,獲取訪問令牌后再調(diào)用API方法。
IdentityServer令牌端對要保護API資源做了配置 new ApiResource("api1", "My API")
限制了訪問Api的客戶端標(biāo)識和訪問資源范圍ClientId = "client", AllowedScopes = { "api1" }還有客戶端需要的秘鑰荣恐。
參考文獻