前言
API裸奔是絕對不允許滴,之前專門針對這塊分享了jwt的解決方案(WebApi接口裸奔有風(fēng)險)渺蒿;那如果是微服務(wù)杂曲,又怎么解決呢竞惋?每一個服務(wù)都加認(rèn)證授權(quán)也可以解決問題闯参,只是顯得認(rèn)證授權(quán)這塊冗余,重復(fù)在搞事情互躬;IT大佬肯定容忍不了开仰,對于微服務(wù)架構(gòu)拟枚,統(tǒng)一的認(rèn)證授權(quán)中心那是必須的。
隨著.NetCore的發(fā)布众弓,IdentityServer4隨之而出恩溅,是.Net Foundation的成員之一,專門針對.NetCore而出的認(rèn)證授權(quán)框架谓娃,當(dāng)前.Net圈是比較火的啦脚乡;再配上微服務(wù)認(rèn)證授權(quán)的必要性,我決定以此開始入手進(jìn)行微服務(wù)架構(gòu)學(xué)習(xí)分享滨达;
主要的學(xué)習(xí)分享思路為敲代碼為向?qū)坛恚绻龅较嚓P(guān)理論概念,結(jié)合代碼案例進(jìn)行解釋捡遍,不在單獨(dú)針對理論知識整理相關(guān)文章(主要是擔(dān)心歸納總結(jié)不好锌订,讓小伙伴疑惑,所以就想著結(jié)合應(yīng)用案例解釋比較容易理解)稽莉。
正文
IdentityServer4 主要的功能就是認(rèn)證和授權(quán),其他功能這里先假裝不知道涩搓;主要目的就是想用其統(tǒng)一保護(hù)各個微服務(wù)的接口污秆;先來理解一下認(rèn)證和授權(quán):
-
授權(quán)(Authorization):在用戶身份認(rèn)證通過之后,授予用戶訪問資源的過程或是用戶授予第三系統(tǒng)訪問自己資源的過程昧甘,資源可能是個人信息良拼、文件、數(shù)據(jù)充边、接口等庸推;OAuth2是現(xiàn)在比較火的授權(quán)標(biāo)準(zhǔn),對于授權(quán)流程浇冰,后續(xù)會舉例說明贬媒;
在公司,假如小伙伴是領(lǐng)導(dǎo)肘习,在出差或休假的時候际乘,通常會通過口頭、郵件漂佩、信息等方式將一些工作臨時委托給某人處理脖含,比如簽字罪塔、參會等,這個過程叫做授權(quán)养葵,如果沒有授權(quán)征堪,簽字無效,也不能隨意參會关拒;
-
認(rèn)證(Authentication):用戶身份認(rèn)證佃蚜,可以將其理解為登錄;系統(tǒng)驗(yàn)證身份憑據(jù)是否合法夏醉,比如用戶名/密碼爽锥、人臉識別等方式;OpenId Connect是目前比較流行的身份認(rèn)證標(biāo)準(zhǔn)協(xié)議畔柔,OpenID是一個去中心化的網(wǎng)上身份認(rèn)證系統(tǒng)氯夷,OpenID Connect是在OAuth2基礎(chǔ)進(jìn)行擴(kuò)展,增加身份認(rèn)證和相關(guān)身份標(biāo)識信息靶擦;
稍微有點(diǎn)規(guī)模的公司腮考,通常都有自己的辦公樓,有專門的保安人員玄捕,管控非公司人員的進(jìn)入踩蔚, 如果是公司人員,刷卡識別即可進(jìn)入枚粘,如果是非公司人員需要登記個人信息確認(rèn)才能進(jìn)入馅闽,這個過程可以理解為身份認(rèn)證;只有驗(yàn)證信息之后才能進(jìn)入公司馍迄。
IdentityServer4 已經(jīng)將OpenID Connect和OAuth 2.0封裝實(shí)現(xiàn)福也,開發(fā)者開箱即用,無需再重新自己實(shí)現(xiàn)細(xì)節(jié)攀圈,但如果有需要暴凑,小伙伴可以在IdentitySever4基礎(chǔ)進(jìn)行擴(kuò)展個性化需求;
在授權(quán)過程中赘来,根據(jù)應(yīng)用場景不同现喳,有四種授權(quán)模式可以選擇,如下:
- Authorization Code(授權(quán)碼):最完整的授權(quán)模式犬辰,也是相對比較安全的模式嗦篱,適用于有后臺的應(yīng)用程序,如MVC項(xiàng)目幌缝;
- Implicit(簡化模式):簡化授權(quán)碼模式默色,適用于無后臺的應(yīng)用程序,如前后端分離項(xiàng)目;
- Resource Owner Password Credentials(資源所有者密碼):直接通過用戶名和密碼獲得授權(quán)腿宰,這種適用于高度信任的應(yīng)用呕诉,因?yàn)樾枰斎胗脩裘兔艽a,安全泄露風(fēng)險高吃度;
- **Client Credentials(客戶端模式) **:這是無用戶操作模式甩挫,適用于機(jī)器對機(jī)器的對接,沒有用戶干預(yù)的應(yīng)用椿每,如后臺任務(wù)調(diào)度應(yīng)用伊者,采集數(shù)據(jù)應(yīng)用等;
- 混合模式:以上四種的組合间护。
其他理論先不說了亦渗,我們邊擼碼邊聊,這樣記憶深刻一點(diǎn)汁尺,這里就從最簡單的Client Credentials開始:
Client Credentials 客戶端授權(quán)模式
客戶端模式?jīng)]有用戶法精,就只是單純的機(jī)器對機(jī)器的交互,大概的流程如下:
流程簡要說明:
- 首先客戶端帶上憑據(jù)向授權(quán)服務(wù)器獲取AccessToken痴突,這里的客戶端憑據(jù)是提前在授權(quán)服務(wù)器上備案過的搂蜓;
- 授權(quán)服務(wù)器驗(yàn)證客戶端憑據(jù),成功之后直接返回AccessToken辽装;
- 客戶端在帶上AccessToken訪問資源服務(wù)器帮碰;
- 資源服務(wù)器正常返回結(jié)果,如果沒有AccessToken是不能訪問受保護(hù)資源的拾积;
來殉挽,結(jié)合流程看看代碼怎么實(shí)現(xiàn),一步一步來:
>>>先創(chuàng)建API項(xiàng)目---資源服務(wù)器
-
創(chuàng)建一個OrderController拓巧,并在里面新增一個Orders 接口斯碌,接口沒有進(jìn)行保護(hù);
image-20201231105706789 -
接口沒有進(jìn)行保護(hù)玲销,可以任意訪問输拇,如下:
image-20201231105517893
>>>再創(chuàng)建認(rèn)證授權(quán)中心項(xiàng)目---授權(quán)服務(wù)器摘符,將資源服務(wù)保護(hù)起來
以上的API接口裸奔是有風(fēng)險的贤斜,現(xiàn)在需要統(tǒng)一的認(rèn)證授權(quán)中心進(jìn)行保護(hù),如下:
-
新創(chuàng)建一個API項(xiàng)目逛裤,并引入IdentityServer4包瘩绒,并在內(nèi)存中模擬相關(guān)數(shù)據(jù),方便測試带族;
image-20201231123450257術(shù)語解釋:
ApiScope:就是一個作用域范圍锁荔,生成的Token只能訪問指定范圍的資源;
Client:這里的客戶端就是應(yīng)用蝙砌,比如MVC項(xiàng)目阳堕、純前端項(xiàng)目跋理、Winfrom/WPF、APP等恬总,必須首先在授權(quán)服務(wù)器中進(jìn)行備案并獲得授權(quán)服務(wù)器分配的標(biāo)識和密碼前普,后續(xù)用于獲取AccessToken;
-
模擬數(shù)據(jù)準(zhǔn)備好了壹堰,就在Startup中進(jìn)行對應(yīng)的注入和配置拭卿,并開啟中間件,如下:
image-20201231124703423 -
這樣就初步完成授權(quán)服務(wù)器的搭建贱纠,這里監(jiān)聽的端口改為6100了峻厚,用Postman先來測測是否能正常獲取Token,如下:
image-20201231133328069可能有新手小伙伴會問谆焊,咋知道是這個地址能獲取token的惠桃? 小伙伴可以在瀏覽器中輸入以下鏈接,即可看見授權(quán)服務(wù)器的相關(guān)信息(授權(quán)服務(wù)器地址+/.well-known/openid-configuration):
image-20201231133558917 -
授權(quán)服務(wù)器已經(jīng)好了懊渡,準(zhǔn)備將資源服務(wù)器接入到授權(quán)服務(wù)器刽射,對API接口進(jìn)行保護(hù)(ApiDemo項(xiàng)目中),如下:
image-20201231135258124注:ApiDemo項(xiàng)目中需要Microsoft.AspNetCore.Authentication.JwtBearer包剃执,因?yàn)轫?xiàng)目是基于.NetCore3.1的誓禁,所以這里引用的包版本為3.1.10。
-
然后在接口上面加上[Authorize]特性肾档,將接口保護(hù)起來摹恰,看運(yùn)行效果如下:
image-20201231135953125 -
在Postman中測試,先獲取AccessToken怒见,然后將獲取的AccessToken加入到Header中請求資源服務(wù)器中受保護(hù)的API俗慈,如下:
image-20201231140952036
>>>真實(shí)客戶端訪問受保護(hù)API---控制臺
建一個控制臺項(xiàng)目,具體步驟如圖:
這里就不用文字說明步驟遣耍,小伙伴一邊看代碼闺阱,一邊看注釋,這樣應(yīng)該比較清晰點(diǎn):
static async Task Main(string[] args)
{
// 1. 創(chuàng)建一個HttpClient用于請求
var client = new HttpClient();
// 2. 獲取授權(quán)服務(wù)器的相關(guān)信息舵变,IdentityModel已經(jīng)將其封裝好了
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:6100");
// 3. 檢查是否請求錯誤
if (disco.IsError)
{
// 錯誤就打印錯誤信息酣溃,然后直接返回
Console.WriteLine(disco.Error);
return;
}
// 4. 通過授權(quán)服務(wù)分配的標(biāo)識,向授權(quán)服務(wù)器請求AccessToken
var tokenResp = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
// 指定獲取token的地址纪隙,IdentityModel進(jìn)行封裝赊豌,直接使用即可
Address = disco.TokenEndpoint,
// 指定授權(quán)服務(wù)器分配的客戶端標(biāo)識
ClientId = "client",
// 指定授權(quán)服務(wù)器分的客戶端密碼
ClientSecret = "ordersecret"
});
// 5. 檢查獲取Token是否成功
if (tokenResp.IsError)
{
// 如果失敗,打印錯誤消息并返回
Console.WriteLine(tokenResp.Error);
return;
}
// 6. 創(chuàng)建一個請求API資源的HttpClient
var apiClient = new HttpClient();
// 7. 將獲取到的Token以Bearer的方案設(shè)置在請求頭中
apiClient.SetBearerToken(tokenResp.AccessToken);
// 8. 向資源服務(wù)器中請求受保護(hù)的API
var contentResp = await apiClient.GetAsync("http://localhost:5000/api/Order");
// 9. 打印對應(yīng)的消息
if (contentResp.IsSuccessStatusCode)
{
var content = await contentResp.Content.ReadAsStringAsync();
Console.WriteLine(JArray.Parse(content));
}
else
{
Console.WriteLine(contentResp.StatusCode);
}
Console.ReadLine();
}
到這里離完成還差一步了绵咱,什么碘饼,資源不是保護(hù)了嗎,受保護(hù)資源也能正常訪問了,還差哪一步艾恼?
在授權(quán)服務(wù)器模擬備案客戶端的時候住涉,是不是指定了訪問資源的作用域,也就是說钠绍,備案過的客戶端只能訪問被授權(quán)的API資源秆吵,而現(xiàn)在拿到的AccessToken都能訪問資源服務(wù)器中所有受保護(hù)的資源,那是因?yàn)橘Y源服務(wù)器中的API資源沒有限制作用域訪問五慈,而在實(shí)際項(xiàng)目中纳寂,并不是拿到AccessToken就能隨便訪問,需要做限制泻拦,繼續(xù)往下看↓↓↓
假如指定的scope值和客戶端在授權(quán)服務(wù)器中備案時設(shè)置的不一樣毙芜,就算獲取到AccessToken也不能正常訪問資源,會報403錯誤争拐,這里我不截圖腋粥,小伙伴下去試試。
可能小伙伴會比較急架曹,這都是啥玩意隘冲,全是硬編碼,垃圾文绑雄; 別別別展辞,說好的學(xué)習(xí)分享嘛,一步一個腳印來嘛万牺,最終肯定是小伙伴想要的罗珍,也是我學(xué)習(xí)的目標(biāo);
關(guān)于客戶端憑據(jù)生成的Token脚粟,在jwt.io網(wǎng)站解析看看覆旱,記錄一下,看看后面有用戶參與的情況核无,生成的Token解析出來會有什么不同呢扣唱,先上個圖(圖中解析出來的屬性之前在WebApi接口裸奔有風(fēng)險有說過):
總結(jié)
從這篇開始,后續(xù)會盡快更新學(xué)習(xí)分享团南,小伙伴們加入一起學(xué)習(xí)噪沙,一起討論。下一篇說說Resource Owner Password Credentials.
一個被程序搞丑的帥小伙已慢,關(guān)注"Code綜藝圈"曲聂,跟我一起學(xué)~