2021-05-12 14:32:11
從零搭建一個IdentityServer——聊聊Asp.net core中的身份驗證與授權
2021-02-03 17:00:09
OpenIDConnect是一個身份驗證服務,而Oauth2.0是一個授權框架,在前面幾篇文章裡通過IdentityServer4實現了基於Oauth2.0的使用者端證書(Client_Credentials)、使用者名稱密碼(Password)的授權流程,同時也實現OpenIDConnect的授權碼(Authorization Code)、隱式流程(Implicit)的身份驗證。
???啥?一會兒是授權一會兒是身份驗證,身份驗證與授權傻傻分不清楚??本文就來聊一聊Asp.net core中的身份驗證與授權。
本文主要內容有:
身份驗證與授權
以前寫過一篇asp.net identity的文章(https://www.cnblogs.com/selimsong/p/7828326.html)已經提到過身份驗證與授權的概念,簡單來說身份驗證就是「是誰」的問題,而授權就是「能不能」的問題,一般來說首先需要知道「是誰」,然後再判斷「能不能」。
這裡舉個生活中常見的小栗子,鎖是門用來保護門內財產的工具,而隨著科技發展現在有了指紋鎖,指紋鎖的特徵是它既可以通過指紋來開鎖,也可以通過鑰匙開鎖,對於指紋開鎖時首先需要錄入指紋並指定一個指紋身份,比如保姆阿姨,首先需要的就是給她錄入指紋,然後允許該指紋在上午6點至晚上10點可以開門,那麼最終保姆阿姨在開門時,授權識別指紋,通過指紋匹配到或者說知道是保姆,這裡就是身份驗證,如果陌生人進行指紋匹配那麼將匹配不到任何身份,但是能否開門還得根據設定的規則,那就是開門時間是否在規定的時間範圍內,滿足條件才能開門,這就是授權。
當然在開門這個問題上還有一個Bug,那就是鑰匙,只要擁有鑰匙,不管是誰都能開門,獲得鑰匙就是獲得授權。
在軟體系統中通常使用的使用者名稱密碼登入實際上就是身份驗證功能,使用者登入後系統就記住這一狀態,後續存取系統時系統就知道「是誰」在存取系統,然後因為已經知道是誰,那麼就可以根據具體存取條件來判斷使用者「能不能」存取資源,這就是授權。
Asp.net core中的身份驗證與授權
首先需要再次明確一下Asp.net core是一個Web框架,它本身就具有一些特性,這其中就包括了身份驗證和授權。
在Asp.net core中的身份驗證和授權是通過中介軟體完成的,而把一箇中介軟體新增到asp.net core的應用程式中一般只需要兩個步驟,第一是對相關中介軟體所需引數及服務進行設定,第二就是將相應的中介軟體新增到請求管道中即可。
下圖為基於OpenIDConnect使用者端程式的身份驗證設定:
設定斷點後,直接存取登入頁面進行登入,在登入資訊提交後我們可以看到User資訊是空的:
登入之後仍然沒有使用者資訊:
但是在ResponseHeader的HeaderSetCookie資訊中我們找到了如下資訊:
看到它即將寫入cookie中帶有它建立的身份資訊載體。這個就是登入生成身份資訊載體的過程,至於登陸後即可存取保護內容,是因為登入完成後做了跳轉,跳轉後將攜帶身份資訊發起請求後既可以完成身份驗證,從而可以存取受保護內容。
注:Identity提供的登入功能最終也是通過HttpContext的拓展方法通過IAuthenticationService來完成的,具體可參考相關原始碼,這裡不在贅述。
自主登入與外部登入
自主登入指的是應用程式本身提供了使用者身份核對(使用者名稱+密碼登入),然後擁有使用者資訊自主權(應用程式儲存了與使用者相關的資訊),最後根據使用者資訊來生成使用者資訊載體的登入方式。如Asp.net core Identity提供的就是一種自主登入方式。
外部登入指的是由第三方程式來對使用者身份核對,並提供相關使用者資訊交由程式本身來生成使用者資訊載體的,或者直接由第三方程式生成使用者資訊載體的方式。
如本系列文章介紹的oidc的身份驗證就是由IdentityServer提供使用者身份核對並提供使用者資訊(UserInfo EndPoint),然後交由使用者端程式來生成身份資訊載體Cookie。
而如果通過IdentityServer直接通過Oauth2.0流程獲得Access Token的方式就相當於由第三方程式生成使用者資訊載體,使用者端直接驗證使用者資訊載體即可完成後續的身份驗證。
Asp.net core身份驗證及授權流程
前面內容詳細介紹了Asp.net core身份驗證相關的一些基礎原理,下面就通過一個流程圖來介紹一下完整的身份驗證和授權流程:
從圖中我們可以找到3個主體分別是:瀏覽器、Asp.net core應用程式以及第三方驗證服務。
整個流程的開始可能是通過存取受保護資源、自主登入系統或者外部登入系統開始,但是登入的目的在於存取受保護資源,下面就簡單對存取受保護資源流程進行梳理:
1. 瀏覽器發起受保護資源存取請求(沒有Cookie).
2. 伺服器對請求進行身份驗證,因為沒有Cookie返回一個失敗結果。
3. 因為驗證結果為失敗,所以沒有ClamsIdentity資訊,賦值到HttpContext.User也為空。
4. 進行授權判斷,因為沒有經過身份驗證,所以呼叫質疑操作(Challenge),由預設的ChallengeScheme決定是自主登入還是外部登入。
5. 如果是自主登入,那麼跳轉到應用登入頁面完成登入,並根據使用者資訊生成ClaimsIdentity。
5. 如果是外部登入,那麼跳轉到第三方登入頁面完成登入,並回到自主應用的回撥地址對第三方返回的code、id_token及access_token進行處理,並獲取使用者資訊,根據獲取的使用者資訊生成ClaimsIdentity。
6. 系統將ClaimsIdentity資訊生成身份資訊載體(Cookie)並重定向回之前存取的資源。
7. 重定向後攜帶身份資訊載體存取受保護資源,如果使用者有許可權,那麼可存取資源,如果沒有許可權返回403禁止存取。
小提示:為什麼asp.net core identity生成的UI程式碼中,外部登入執行的核心程式碼為ChallegeResult + (provider 和returnUrl)?
Asp.net core中的授權
前面詳細介紹了Asp.net core中的身份驗證,授權僅僅是其中的一環來幫助完成身份驗證。那麼Asp.net core中提供了哪些授權機制或者說要如何進行授權呢?
Asp.net core及Identity元件提供了簡單的(只要通過身份驗證)、基於角色的、基於宣告(Claim)的、基於策略的授權機制,具體使用方式參考檔案:https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-5.0
另外還給了一個如何實現資料增刪改許可權控制的例子:
上面這個例子告訴我們授權機制不僅僅侷限於授權特性和中介軟體,我們可以把授權機制融入到我們的業務邏輯中。
小結
本篇文章從Asp.Net core介紹了身份驗證和授權的基本概念和原理,通過流程圖的方式展現了Asp.net core身份驗證和授權的流程,最後簡單介紹了授權的相關機制。
現在我們回到文章開頭問的問題為什麼IdentityServer4提供的功能中一會兒是身份驗證,一會兒是授權??
這個問題需要根據主體來看,首先我們看Oauth2.0,它的最終結果是一個Jwt的Bearer Token,這相當於給了你一把鑰匙,使用這個鑰匙你可以開啟指定的門,所以它是一個授權。
然後來看看OIDC的授權碼流程,它除了Access Token外實際上關鍵的是Id_token,證實了使用者的身份,這相當於告訴你,使用者是保姆阿姨,解決了「是誰」的問題,所以是身份驗證。知道了是誰,至於開不開門,那是你(使用者端程式)的授權問題。
最後來看看Asp.net core應用程式,在Asp.net core應用程式中不存在獨立的授權,換句話就是沒法單獨使用授權功能,需要身份驗證和授權功能聯合使用,比如Oauth給了一把鑰匙,但是Asp.net core仍要對鑰匙進行驗證,看清楚鑰匙上貼了張三的名字,但很有可能這把鑰匙是李四拿著。
參考:
https://docs.microsoft.com/en-us/aspnet/core/security/authorization/introduction?view=aspnetcore-5.0
以及文章中涉及的相關原始碼
相關文章