[原創(chuàng)] IdentityServer4權(quán)限控制---客戶端創(chuàng)建、獲取TOKEN及訪問(wèn)API資源(三)
經(jīng)過(guò)前面兩節(jié)課,我們已經(jīng)完成了API服務(wù)器的搭建與IDS4身份驗(yàn)證服務(wù)器的搭建,如果還沒(méi)有看的朋友請(qǐng)到這里圍觀:
[原創(chuàng)] IdentityServer4權(quán)限控制---客戶端授權(quán)模式之API服務(wù)器搭建(一)
[原創(chuàng)] IdentityServer4權(quán)限控制---客戶端授權(quán)模式之IDS4認(rèn)證服務(wù)器搭建(二)
API服務(wù)器是我們要保護(hù)的資源服務(wù)器,我們希望只授權(quán)給通過(guò)身份驗(yàn)證的客戶端去訪問(wèn),而身份驗(yàn)證的工作是由我們搭建的IDS4服務(wù)器來(lái)完成的?,F(xiàn)在我們模擬一個(gè)場(chǎng)景,我們已經(jīng)將上面兩臺(tái)服務(wù)器成功部署到公司的服務(wù)器上去了,現(xiàn)在有個(gè)第三方用戶,他開(kāi)發(fā)了一頭桌面應(yīng)用,叫做client.exe,該應(yīng)用在成功運(yùn)行以后要讀取我們的資源服務(wù)器上的接口數(shù)據(jù),也就是訪問(wèn) https://localhost:6001/上面的三個(gè)接口數(shù)據(jù),我們希望只對(duì)身份已經(jīng)證的用戶開(kāi)放,并不是隨便人寫(xiě)幾行代碼就把我們的數(shù)據(jù)給偷走了,這里CLIENT.EXE就是我們的客戶端,為此我簡(jiǎn)單畫(huà)了一張圖,如下:
(注意:步驟4、5、5'是假想的,我們要通過(guò)實(shí)驗(yàn)去驗(yàn)證它是否存在)
客戶端程序?yàn)榱嗽L問(wèn)API資源服務(wù)器上的接口數(shù)據(jù),先要進(jìn)行身份驗(yàn)證才可以,不然會(huì)被資源服務(wù)器拒絕,所以,它要先去IDS4服務(wù)器申請(qǐng)TOKEN,申請(qǐng)TOKEN需要攜帶必要的身份信息,上圖左下角橘色框中便是。成功拿到TOKEN以后,客戶端再發(fā)起一個(gè)網(wǎng)絡(luò)連接,去申請(qǐng)API資源,這個(gè)過(guò)程中要向API資源服務(wù)器提交身份認(rèn)證用的TOKEN,右下角綠色便是。為了流程理解方便,我在上面標(biāo)注了數(shù)據(jù)代表步驟順序。資源服務(wù)器得到TOKEN以后,是JWT格式的,直接在本機(jī)驗(yàn)證,(這里要特別注意一下?。?!,也就是說(shuō):步驟3以后直接返回,而不是通過(guò)步驟45后再返回)。
現(xiàn)在我們需要去打造一個(gè)客戶端client.exe去申請(qǐng)我們的TOKEN以及訪問(wèn)我們的資源。
創(chuàng)建一個(gè)控制臺(tái)程序
選擇新建目錄
選擇.NET6.0下一步,就建好了。我們的直接用官網(wǎng)提供的代碼去測(cè)試,如下,
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. using IdentityModel.Client; using Newtonsoft.Json.Linq; using System; using System.Net.Http; using System.Threading.Tasks; namespace Client { public class Program { private static async Task Main() { // discover endpoints from metadata var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001"); //取得證書(shū) https://localhost:5001/.well-known/openid-configuration if (disco.IsError) { Console.WriteLine(disco.Error); return; } // 請(qǐng)求token令牌 var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" //Scope = "identity" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json);//輸出令牌信息 Console.WriteLine("\n\n"); // call api 訪問(wèn)我們的API資源,注意,每個(gè)資源的權(quán)限要求是不一樣的 var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("https://localhost:6001/identity"); //該資源要求登錄用戶才可以訪問(wèn) if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } response = await apiClient.GetAsync("https://localhost:6001/api/Students"); //該資源匿名可訪問(wèn) if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } } }我們把IdentityModel包引起去,再把Newtonsoft.Json也引進(jìn)去,在第二課中講到,Identity 接口是要驗(yàn)證用戶才可以訪問(wèn)成功的,未登錄用戶返回401?,F(xiàn)在我們運(yùn)行client.exe試試,運(yùn)行之前先運(yùn)行我們的API服務(wù)器與身份驗(yàn)證服務(wù)器,再運(yùn)行client.exe,看到CMD窗口返回以下信息:
{"access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6IkU1OEMxMkExMUU3NTkwRTk3NEY3REREQTA3NEIwRTUwIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NjIxOTcyMzMsImV4cCI6MTY2MjIwMDgzMywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NTAwMSIsImNsaWVudF9pZCI6ImNsaWVudCIsImp0aSI6IjU3RDA4MkIxN0NFRjUxM0M4MzVFRENFNzkxQ0NGRDJGIiwiaWF0IjoxNjYyMTk3MjMzLCJzY29wZSI6WyJhcGkxIl19.pYY8nTQvHQMeKjn5LkE-OO2xzsnS4xyicusJ88SMyThqelFq95fhqZC3jUoOFqDUy8ex7S1Q_c8RL0G3dnvuIIiyg6ECPxRBNHwVfztdzVpoe7qOEyYdP_Z9lqwcgJdbWiWCGzcPkWzErikFIHDe2KwprD1_YlDBN67ze96sVWypNrSBWhFgPf80B3wfDSE3z1apoz0Pe8QqkoWpE9xT-Y0_vz5s3m-CaNl6ar8M1Wk2LdvDYwk3UgJ5p4XViwtookPUj48rKVseohe7qQtTpXpT0RXf0JLuXmAhxRMweXBZMNY64F-d9MbqftyBKlDffXTgoK8JeaujcYdZ9nLy6A","expires_in":3600,"token_type":"Bearer","scope":"api1"} [ { "type": "nbf", "value": "1662197233" }, { "type": "exp", "value": "1662200833" }, { "type": "iss", "value": "https://localhost:5001" }, { "type": "client_id", "value": "client" }, { "type": "jti", "value": "57D082B17CEF513C835EDCE791CCFD2F" }, { "type": "iat", "value": "1662197233" }, { "type": "scope", "value": "api1" } ] [ { "boinYear": 1986, "xingMing": "QiQi", "age": 36, "address": "中國(guó)" }, { "boinYear": 1970, "xingMing": "Black Smith", "age": 52, "address": "英國(guó)" }, { "boinYear": 1957, "xingMing": "John", "age": 65, "address": "日本" }, { "boinYear": 1975, "xingMing": "John", "age": 47, "address": "加拿大" }, { "boinYear": 1975, "xingMing": "QiQi", "age": 47, "address": "美國(guó)" } ]
LOOK,我們成功獲取到了Identity接口的數(shù)據(jù)!我們接著文章開(kāi)始處留下的問(wèn)題分別做這樣的實(shí)驗(yàn):
一、在 client.exe從identityserver4獲得tokenResponse.AccessToken后,將identityserver4服務(wù)器關(guān)閉,分別訪問(wèn)兩個(gè)API接口。
二、在獲得tokenResponse.AccessToken后,將它保存下來(lái),然后關(guān)閉identityserver4服務(wù)器,也就是說(shuō),用上次獲得到TOKEN,在驗(yàn)證服務(wù)器關(guān)閉的狀態(tài)下去訪問(wèn)API資源。
通過(guò)上面的實(shí)驗(yàn),我們知道,在identityserver4關(guān)閉的時(shí)候,我們的身份驗(yàn)證照常可以通過(guò),也就是說(shuō),登錄獲得TOKEN是在Identityserver4上進(jìn)行的,而獲得TOKEN以后就沒(méi)它什么事了。文章開(kāi)始處的流程圖中4、5、5‘ 步驟是不存在的。
這樣,我們的客戶端就只有通過(guò)了身份驗(yàn)證才可以訪問(wèn)到我們的資源,也就是說(shuō)資源得到了保護(hù)。但是很多小伙伴要問(wèn)了,你這也太粗暴了吧!我怎么樣對(duì)API的資源進(jìn)行更精細(xì)化的保護(hù)呢? 別著急,我們打開(kāi)APITEST項(xiàng)目,在program.cs 中寫(xiě)一個(gè)權(quán)限策略來(lái)實(shí)現(xiàn)更精細(xì)化的控制。
builder.Services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "api1"); }); });就這樣,我們?cè)趐rogram.cs啟動(dòng)之時(shí)注冊(cè)了一個(gè)安全策略,我們只需要在需要驗(yàn)證的地放這樣去引用就可以了
//[Authorize] [Authorize(Policy = "ApiScope")]這樣就不是所有登錄的用戶都可以訪問(wèn)我們的資源了,而是需要滿足ApiScope策略的用戶才可以!根據(jù)這個(gè)原理,我們可以擴(kuò)展很多。IDS4官方宣稱,給資源一個(gè)別名是比較推薦的一種做法,給了別名,其實(shí)就可以用不同的別名來(lái)授權(quán)。嘗試將API服務(wù)器換成域名,和IDS4分別不同的域名,讓客戶端跨域申請(qǐng)TOKEN及授權(quán),任然是可以的,看來(lái)還是比較方便的,其它功能等后面研究再深入一些,我再回來(lái)補(bǔ)充。。。
今天的源碼下載
看文章一天,寫(xiě)文章一天,寫(xiě)作不易,朋友們留言支持一下就不勝感激了。。。
原創(chuàng)文章,創(chuàng)作不易,轉(zhuǎn)載請(qǐng)注明出處:http://www.maiyt.com/article-29