寫在前面
1、源碼(.Net Core 2.2)
git地址:https://github.com/yizhaoxian/CoreIdentityServer4Demo.git
2、相關章節
2.1、《IdentityServer4 (1) 客戶端授權模式(Client Credentials)》
2.2、《IdentityServer4 (2) 密碼授權(Resource Owner Password)》
2.3、《IdentityServer4 (3) 授權碼模式(Authorization Code)》
2.4、《IdentityServer4 (4) 靜默刷新(Implicit)》
2.5、《IdentityServer4 (5) 混合模式(Hybrid)》
3、參考資料
IdentityServer4 中文文檔 http://www.identityserver.com.cn/
IdentityServer4 英文文檔 https://identityserver4.readthedocs.io/en/latest/
4、流程圖
此文章是在上一篇文章的基礎上繼續修改的,基礎代碼請查看上一篇文章《IdentityServer4(1)客戶端授權模式》
密碼授權模式,允許一個客戶端發送用戶名和密碼到令牌服務並獲得一個表示該用戶的訪問令牌(AccessToken),這里多了一個概念就是【用戶】,賬號密碼需要用戶提供給客戶端
一、IdentityServer修改
1、添加一個新的客戶端,IdpConfig.GetClients()
new Client { // 客戶端ID 這個很重要 ClientId = "client pwd", //資源所有者密碼授權客戶端定義 AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, // 用於認證的密碼 ClientSecrets = { new Secret("secret".Sha256()) }, // 客戶端有權訪問的范圍(Scopes) AllowedScopes = { "api1", IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Address, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Phone } }
2、添加測試用戶
新建一個類 TestUsers.cs
public class TestUsers { public static List<TestUser> Users { get { var address = new { street_address = "ChaoYang", locality = "BeiJing", postal_code = 10010, country = "China" }; return new List<TestUser> { new TestUser { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new Claim(JwtClaimTypes.Name, "TestUser.Alice Smith"), new Claim(JwtClaimTypes.GivenName, "TestUser.Alice"), new Claim(JwtClaimTypes.FamilyName, "TestUser.Smith"), new Claim(JwtClaimTypes.PhoneNumber, "TestUser.13812345678"), new Claim(JwtClaimTypes.Email, "TestUser.AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "TestUser.http://alice.com"), new Claim(JwtClaimTypes.Address, JsonConvert.SerializeObject(address), IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, JsonConvert.SerializeObject(address), IdentityServerConstants.ClaimValueTypes.Json) } } }; } } }
3、注冊相關信息
StartUp.cs 添加測試用戶和用戶認證信息
public void ConfigureServices(IServiceCollection services) { services.AddIdentityServer() .AddDeveloperSigningCredential() //添加測試用戶 .AddTestUsers(TestUsers.Users) //添加用戶認證信息 .AddInMemoryIdentityResources(IdpConfig.GetApiResources()) .AddInMemoryApiResources(IdpConfig.GetApis()) .AddInMemoryClients(IdpConfig.GetClients()); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); }
二、API修改
1、修改 SuiBianController Get() 返回內容
隨意這里改不改無所謂,因為我下面截圖,和上一篇對不上 所以在這里說明一下
[HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); }
三、客戶端修改
1、添加一個 Action 請求 AccessToken
public async Task<IActionResult> TokenPwd() { var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync(_idpBaseUrl); if (disco.IsError) { return Content("獲取發現文檔失敗。error:" + disco.Error); } #region 第一種方式請求 token //var tokenclient = new TokenClient(client, new TokenClientOptions //{ // ClientId = "client pwd", // ClientSecret = "secret", // Address = disco.TokenEndpoint, //}); //var token = await tokenclient.RequestPasswordTokenAsync("alice", "alice", "api1"); #endregion var token = await client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = disco.TokenEndpoint, //下面2個屬性對應的是 IdentityServer定義的測試用戶,這里應是 Action 參數傳遞進來的,為了方便直接寫死的 UserName = "alice", Password = "alice", //下面3個屬性對應的是 IdentityServer定義的客戶端 ClientId = "client pwd", ClientSecret = "secret", Scope = "api1 openid profile email phone address" }); if (token.IsError) { return Content("獲取 AccessToken 失敗。error:" + token.Error); } //將token 臨時存儲到 緩存中 _memoryCache.Set("AccessToken_Pwd", token.AccessToken); return Content("獲取 AccessToken 成功。Token:" + token.AccessToken); }
2、添加一個Action 測試請求 api1
public async Task<IActionResult> SuiBianPwd() { string token, apiurl = GetApiUrl("suibian"); _memoryCache.TryGetValue("AccessToken_Pwd", out token); if (string.IsNullOrEmpty(token)) { return Content("token is null"); } var client = new HttpClient(); client.SetBearerToken(token); var response = await client.GetAsync(apiurl); var result = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { _memoryCache.Remove("AccessToken"); return Content($"獲取 {apiurl} 失敗。StatusCode:{response.StatusCode} \r\n Token:{token} \r\n result:{result}"); } return Json(JsonConvert.DeserializeObject(result)); }
3、添加一個Action 測試獲取用戶認證信息
public async Task<IActionResult> IdentityInfoPwd() { string token; _memoryCache.TryGetValue("AccessToken_Pwd", out token); if (string.IsNullOrEmpty(token)) { return Content("token is null"); } var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync(_idpBaseUrl); if (disco.IsError) { return Content("獲取發現文檔失敗。error:" + disco.Error); } client.SetBearerToken(token); var response = await client.GetAsync(disco.UserInfoEndpoint); var result = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { _memoryCache.Remove("AccessToken"); return Content($"獲取 UserInfo 失敗。StatusCode:{response.StatusCode} \r\n Token:{token} \r\n result:{result}"); } return Json(JsonConvert.DeserializeObject(result)); }
三、客戶端測試
1、獲取 token
訪問 http://localhost:5003/Idp/tokenpwd 獲取token成功
2、請求 api1
訪問 http://localhost:5003/Idp/suibianpwd 獲取api信息成功
3、獲取用戶認證信息
訪問 http://localhost:5003/Idp/identityinfopwd 獲取成功