一.概述 OAuth 2.0資源(web api)所有者密碼授權,允許客戶端(Client項目)向令牌服務(IdentityServer項目)發送用戶名和密碼,並獲取代表該用戶的訪問令牌。在官方文檔中講到:規範通常建議不要使用“資源所有者密碼授權”。當用戶進行身份驗證並請求訪問令牌時,使用一個互動式 ...
一.概述
OAuth 2.0資源(web api)所有者密碼授權,允許客戶端(Client項目)向令牌服務(IdentityServer項目)發送用戶名和密碼,並獲取代表該用戶的訪問令牌。在官方文檔中講到:規範通常建議不要使用“資源所有者密碼授權”。當用戶進行身份驗證並請求訪問令牌時,使用一個互動式OpenID Connect流程通常要好得多(下篇再瞭解)。
本篇介紹“資源所有者密碼授權”是因為這種授權允許我們快速啟動IdentityServer。開源地址:Github
下麵示例與官方示例有點區別,該示例使用了Identity密碼保護API。關於asp.net core Identity的瞭解實現,查看之前章節或官方文檔。示例中分別是IdentityServer令牌項目、 API資源項目、 Client訪問項目。與上篇相比一樣,還是三個項目,區別在於:
(1) IdentityServer令牌項目換成了含有asp.net core Identity的MVC項目。
(2) API資源項目沒有變動。
(3) Client訪問項目使用了用戶名和密碼訪問受保護的API。
二. IdentityServer項目
IdentityServer令牌項目是包含了 Identity功能(安裝:Install-Package IdentityServer4),在項目中,添加了Config.cs類和Startup.cs中加入了IdentityServer的啟動配置。下麵是MVC項目目錄結構:
(1) 添加用戶
IdentityServer類庫中自帶TestUser測試類,是DTO數據傳輸對象,存儲用戶及其聲明(claims)。TestUser是用於測試中的記憶體(In-memory)用戶對象。在正式環境下,獲取資料庫中的用戶表(User),需要結合IdentityServer的IResourceOwnerPasswordValidator介面(不再本篇講述中)。 下麵通過在config.cs類中添加GetUsers方法獲取用戶密碼,存儲在TestUser數據傳輸對象中。
/// <summary> ///獲取用戶,這些用戶可以訪問受密碼保護的API /// </summary> /// <param name="provider"></param> /// <returns></returns> public static List<TestUser> GetUsers(ServiceProvider provider) { var webAppIdentityDemoUser = provider.GetRequiredService<UserManager<WebAppIdentityDemoUser>>(); IList<WebAppIdentityDemoUser> users = null; //獲取Identity的User表用戶,條件是屬於Administrator角色的用戶 users = webAppIdentityDemoUser.GetUsersInRoleAsync("Administrator").Result; List<TestUser> testUserList = new List<TestUser>(); foreach (WebAppIdentityDemoUser user in users) { testUserList.Add(new TestUser() { SubjectId = user.Id.ToString(), Username = user.UserName, Password = user.PasswordHash }); } return testUserList; }
(2) 然後在Startup類的ConfigureServices方法中使用IdentityServer註入測試用戶:
ServiceProvider provider = services.BuildServiceProvider(); var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.GetIdentityResources()) .AddInMemoryApiResources(Config.GetApis()) .AddInMemoryClients(Config.GetClients()) .AddTestUsers(Config.GetUsers(provider));
(3) 定義客戶端, 使用密碼授予訪問此API(資源範圍:api1)
在config.cs類中,定義客戶端,通過修改AllowedGrantTypes枚舉來簡單地向現有客戶端添加對授權類型的支持, 將以下代碼添加到客戶端配置中, 裡面支持二個Client授權類型,分別是ClientCredentials使用憑證來訪問令牌和ResourceOwnerPassword 使用密碼來訪問令牌。
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()) }, // scopes that client has access to AllowedScopes = { "api1" } }, // resource owner password grant client new Client { ClientId = "ro.client", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, ClientSecrets = { new Secret("secret".Sha256()) }, AllowedScopes = { "api1" } } }; }
三.Client項目
該Client項目類似於上篇介紹的Client項目,該項目名為ResourceOwnerClient, 該Client將收集用戶名和密碼,併在令牌請求期間,將其發送到IdentityServer令牌服務(WebAppIdentityDemo項目)
// request token 請求令牌 var tokenResponse = await client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = disco.TokenEndpoint, ClientId = "ro.client", ClientSecret = "secret", UserName = "[email protected]", Password = "AQAAAAEAACcQAAAAEH4Xhui5BByq6d8VS5Z+S2o2SnlkyrP5pN9CmMpgJ4QiIVrt7lBLzDlEWa6AdlpxpA==", Scope = "api1" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n");
最後測試,先啟動WebAppIdentityDemo項目程式,再啟動API程式,最後啟動Client客戶端來訪問API,通過下圖可以瞭解到:(1)客戶端請求使用“用戶名和和密碼”訪問令牌(token)成功, (2) 客戶端使用令牌(AccessToken)來訪問受密碼保護的web API介面成功。
參考文獻