[原創(chuàng)] IdentityServer4權限控制---客戶端創(chuàng)建、獲取TOKEN及訪問API資源(三)
經過前面兩節(jié)課,我們已經完成了API服務器的搭建與IDS4身份驗證服務器的搭建,如果還沒有看的朋友請到這里圍觀:
[原創(chuàng)] IdentityServer4權限控制---客戶端授權模式之API服務器搭建(一)
[原創(chuàng)] IdentityServer4權限控制---客戶端授權模式之IDS4認證服務器搭建(二)
API服務器是我們要保護的資源服務器,我們希望只授權給通過身份驗證的客戶端去訪問,而身份驗證的工作是由我們搭建的IDS4服務器來完成的。現(xiàn)在我們模擬一個場景,我們已經將上面兩臺服務器成功部署到公司的服務器上去了,現(xiàn)在有個第三方用戶,他開發(fā)了一頭桌面應用,叫做client.exe,該應用在成功運行以后要讀取我們的資源服務器上的接口數據,也就是訪問 https://localhost:6001/上面的三個接口數據,我們希望只對身份已經證的用戶開放,并不是隨便人寫幾行代碼就把我們的數據給偷走了,這里CLIENT.EXE就是我們的客戶端,為此我簡單畫了一張圖,如下:
(注意:步驟4、5、5'是假想的,我們要通過實驗去驗證它是否存在)
客戶端程序為了訪問API資源服務器上的接口數據,先要進行身份驗證才可以,不然會被資源服務器拒絕,所以,它要先去IDS4服務器申請TOKEN,申請TOKEN需要攜帶必要的身份信息,上圖左下角橘色框中便是。成功拿到TOKEN以后,客戶端再發(fā)起一個網絡連接,去申請API資源,這個過程中要向API資源服務器提交身份認證用的TOKEN,右下角綠色便是。為了流程理解方便,我在上面標注了數據代表步驟順序。資源服務器得到TOKEN以后,是JWT格式的,直接在本機驗證,(這里要特別注意一下?。?!,也就是說:步驟3以后直接返回,而不是通過步驟45后再返回)。
現(xiàn)在我們需要去打造一個客戶端client.exe去申請我們的TOKEN以及訪問我們的資源。
創(chuàng)建一個控制臺程序
選擇新建目錄
選擇.NET6.0下一步,就建好了。我們的直接用官網提供的代碼去測試,如下,
// 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"); //取得證書 https://localhost:5001/.well-known/openid-configuration if (disco.IsError) { Console.WriteLine(disco.Error); return; } // 請求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 訪問我們的API資源,注意,每個資源的權限要求是不一樣的 var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("https://localhost:6001/identity"); //該資源要求登錄用戶才可以訪問 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"); //該資源匿名可訪問 if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } } } }我們把IdentityModel包引起去,再把Newtonsoft.Json也引進去,在第二課中講到,Identity 接口是要驗證用戶才可以訪問成功的,未登錄用戶返回401?,F(xiàn)在我們運行client.exe試試,運行之前先運行我們的API服務器與身份驗證服務器,再運行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": "中國" }, { "boinYear": 1970, "xingMing": "Black Smith", "age": 52, "address": "英國" }, { "boinYear": 1957, "xingMing": "John", "age": 65, "address": "日本" }, { "boinYear": 1975, "xingMing": "John", "age": 47, "address": "加拿大" }, { "boinYear": 1975, "xingMing": "QiQi", "age": 47, "address": "美國" } ]
LOOK,我們成功獲取到了Identity接口的數據!我們接著文章開始處留下的問題分別做這樣的實驗:
一、在 client.exe從identityserver4獲得tokenResponse.AccessToken后,將identityserver4服務器關閉,分別訪問兩個API接口。
二、在獲得tokenResponse.AccessToken后,將它保存下來,然后關閉identityserver4服務器,也就是說,用上次獲得到TOKEN,在驗證服務器關閉的狀態(tài)下去訪問API資源。
通過上面的實驗,我們知道,在identityserver4關閉的時候,我們的身份驗證照??梢酝ㄟ^,也就是說,登錄獲得TOKEN是在Identityserver4上進行的,而獲得TOKEN以后就沒它什么事了。文章開始處的流程圖中4、5、5‘ 步驟是不存在的。
這樣,我們的客戶端就只有通過了身份驗證才可以訪問到我們的資源,也就是說資源得到了保護。但是很多小伙伴要問了,你這也太粗暴了吧!我怎么樣對API的資源進行更精細化的保護呢? 別著急,我們打開APITEST項目,在program.cs 中寫一個權限策略來實現(xiàn)更精細化的控制。
builder.Services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "api1"); }); });就這樣,我們在program.cs啟動之時注冊了一個安全策略,我們只需要在需要驗證的地放這樣去引用就可以了
//[Authorize] [Authorize(Policy = "ApiScope")]這樣就不是所有登錄的用戶都可以訪問我們的資源了,而是需要滿足ApiScope策略的用戶才可以!根據這個原理,我們可以擴展很多。IDS4官方宣稱,給資源一個別名是比較推薦的一種做法,給了別名,其實就可以用不同的別名來授權。嘗試將API服務器換成域名,和IDS4分別不同的域名,讓客戶端跨域申請TOKEN及授權,任然是可以的,看來還是比較方便的,其它功能等后面研究再深入一些,我再回來補充。。。
今天的源碼下載
看文章一天,寫文章一天,寫作不易,朋友們留言支持一下就不勝感激了。。。
原創(chuàng)文章,創(chuàng)作不易,轉載請注明出處:http://www.vrdss.com/article-29