1. 程式人生 > >asp.net core 3.x 身份驗證-1涉及到的概念

asp.net core 3.x 身份驗證-1涉及到的概念

前言

從本篇開始將圍繞asp.net core身份驗證寫個小系列,希望你看完本系列後,腦子裡對asp.net core的身份驗證原理有個大致印象。
至於身份驗證是啥?與授權有啥聯絡?就不介紹了,太囉嗦。你如果不曉得,自己去搜搜吧。
我的學習思路是詳細看原始碼 > 總結得出一個巨集觀上的印象 + 如何使用。
如果發現有啥講錯的望指正,免得誤導觀眾

我們偶爾會思考如何設計一個牛X的軟體,其實通過對asp.net core框架本身的學習更划算,一來我們熟悉了asp.net core框架,再者我們學習了微軟碰到需求是如何設計的。

計劃:

  • 基本介紹 - 概述 + 核心類介紹
  • 基於cookie/session的身份驗證原理 - 適合瀏覽器
  • 基於Token身份驗證 - 適合移動端app
  • 整合第三方登入原理 - 比如整合微信、支付寶登入
  • IdentityServer - 目前不鳥解
  • asp.net core Identity - 目前不鳥解

必備知識:asp.net core、配置、選項、依賴注入、中介軟體等...

參考:原始碼、Artech、mvc5基於owin的身份驗證視訊

注意:本篇只講涉及到的幾個概念

 

身份驗證方式和簡易流程

常見的身份驗證方式:

  • 基於cookie/session的身份驗證原理 - 適合瀏覽器
  • 基於Token身份驗證 - 適合移動端app
  • 整合第三方登入原理 - 比如整合微信、支付寶登入

為了便於理解後續的概念,下面先以最常見的 使用者密碼+cookie的身份驗證方式說說核心流程

登入:

  1. 使用者輸入賬號密碼提交
  2. 服務端驗證賬號密碼
  3. 若驗證成功,則建立一個包含使用者標識的票證(下面會說)
  4. 將票證加密成字串寫入cookie

攜帶cookie請求:

  1. 使用者發起請求
  2. 身份驗證中介軟體嘗試獲取並解密cookie,進而得到含使用者標識的票證(下面會說)
  3. 將使用者標識設定到HttpContext.User屬性

注意:若身份驗證中介軟體即使沒有解析得到使用者標識,請求也會繼續執行,此時以匿名使用者的身份在訪問系統

 

使用者標識ClaimsPrincipal

它用來表示當前登入的使用者,它包含使用者Id + 一些與許可權檢查相關的附件屬性(角色、所屬部門)。
當請求抵達時“身份驗證中介軟體”將從請求中解析得到當前使用者,如果獲取成功則賦值給HttpContext.User屬性

所以對於我們來說通常有兩個場景使用它
在任意能訪問HttpContext的地方獲取當前使用者,比如在Controller中。
如果需要自定義實現身份驗證,則我們要想方設法從請求中解析得到使用者,並賦值給HttpContext.User

現在你至少對使用者標識這個概念有點理解了,如果要刨根問底兒就自行搜尋關鍵字:asp.net Claims

也許你曾經做過或見過這樣的設計,定義Employee表示當前系統的使用者,當用戶登入時會從資料庫查詢得到對應的Employee,若賬號密碼驗證通過則將其放入Session或快取中。下次訪問時直接從Session/快取中獲取當前使用者。個人覺得這種設計存在如下問題:

  • 浪費記憶體:我們的業務程式碼訪問當前使用者最多的欄位可能只是使用者id,性別、地址、聯絡電話、學歷....這些欄位不是每個業務處理都需要的
  • 拋棄了asp.net身份驗證框架:從asp.net 2.0時代微軟就設計了IPrincipal,後續的版本直到mvc5中基於owin的身份驗證都在使用此介面,後續的許可權驗證微軟也提供了,也是基於此介面的,但我們放棄了,反而是自己有寫了一套微軟本身就實現的功能,可能多數是覺得自己寫的更簡單。但我覺得判斷哪種方式更合適是在你對兩種方式都瞭解的情況下再做出判斷。

 

使用者票證AuthenticationTicket

既然有了上面的使用者標識,何不直接在登入時加密這個標識,解析時直接解密得到呢?因為我們還需要額外的控制,比如過期時間,這個屬性只是在身份驗證階段來判斷是否過期,在我們(如Controller.Action中)使用使用者標識的時候並不需要此欄位,類似的額外欄位根據不同的身份驗證方式可能有很多,因此定義了“使用者票證”這個概念,它包含 使用者標識 + 身份驗證過程中需要的額外屬性(如得到使用者標識的時間、過期時間等)

 

身份驗證處理器AuthenticationHandler

參考上面的使用者名稱密碼+cookie身份驗證流程我們發現有幾個核心的處理步驟:

  • 在登入時驗證通過後將使用者標識加密後儲存到cookie,SignIn
  • 當用戶登出時,需要清楚代表使用者標識的cookie,SignOut
  • 在登入時從請求中獲取使用者標識,Authenticate
  • 在使用者未登入訪問受保護的資源時,我們希望跳轉到到登入頁,Challenge
    • Challenge叫做質詢/挑戰,意思是當發現沒有從當前請求中發現使用者標識是希望怎麼辦,可能是跳轉到登入頁,也可能是直接響應401,或者跳轉到第三方(如QQ、微信)的登入頁 
  • 因為某種原因(如許可權驗證不過),阻止方案,Forbid

身份驗證處理器就是用來定義這些步驟的
asp.net core中定義了對應的介面、和抽象類,不同的身份驗證方式有不的實現類,比如基於token的身份驗證方式在SignIn時直接加密票證然後返回這個字串(而不是寫入cookie)
這些步驟都是圍繞身份驗證來定義的,在不同地方來呼叫(比如在登入頁對於的Action、在請求抵達時、在授權中介軟體中)

 

身份驗證選項AuthenticationSchemeOptions

在上述身份驗證處理的多個步驟中會用到一些選項資料,比如基於cookie的身份驗證 cookeName、有效時長、再比如從請求時從cookie中解析得到使用者標識後回撥選項中的某個回撥函式,允許我們的程式碼向除錯中新增額外資料,或者乾脆替換整個標識。

所以身份驗證選項用來允許我們控制AuthenticationHandler的執行。不同的身份驗證方式有不同的選項物件,它們直接或間接實現AuthenticationSchemeOptions

 

身份驗證方案AuthenticationScheme

總結性的說:身份驗證方案 = 名稱 + 身份驗證處理器型別,暫時可以理解一種身份驗證方式 對應 一個身份驗證方案,比如:
基於使用者名稱密碼+cookie的身份驗證方式 對應的 身份驗證方案為:new AuthenticationScheme("UIDPWDCookie",typeof(CookieAuthenticationHandler))
基於使用者名稱密碼+token  的身份驗證方式 對應的 身份驗證方案為:new AuthenticationScheme("JwtBearer",typeof(JwtBearerHandler))

身份驗證方案在程式啟動階段配置,啟動後形成一個身份驗證方案列表。
程式執行階段從這個列表中取出制定方案,得到對應的處理器型別,然後建立它,最後呼叫這個處理器做相應處理
比如登入操作的Action中xxx.SignIn("方案名") > 通過方案名找到方案從而得到對應的處理器型別 > 建立處理器 > 呼叫其SignIn方法

一種特殊的情況可能多種方案使用同一個身份驗證處理器型別,這個後續的整合第三方登入來說

 

身份驗證方案的容器AuthenticationSchemeProvider

我們說一個系統可能同時支援多種身份驗證方案,因此我們需要一個容器,可以把它理解為Dictionary<方案名,身份驗證方案>

 

身份驗證處理器容器AuthenticationHandlerProvider

可以暫時把它理解為Dictionary<方案名, 身份驗證處理器容器>,因為這個物件是每次請求都會建立,並且它提供AuthenticationHandler時時從方案列表中去找到制定方案從而得到對應的處理器型別然後建立的。如果不理解沒關係,下一篇講流程就懂了

 

身份驗證服務AuthenticationService

身份驗證中的步驟是在多個地方被呼叫的,身份驗證中介軟體、授權中介軟體、登入的Action(如:AccountController.SignIn())、登出的Action(如:AccountController.SignOut()),身份驗證的核心方法定義在這個類中,但它本質上還是去找到對應的身份驗證處理器並呼叫其同名方法。其實這些方法還進一步以擴充套件方法的形式定義到HttpContext上了。以SignIn方法為例
HttpContext.SignIn() > AuthenticationService.SignIn() > AuthenticationHandler.SignIn() 

 

後續

這一篇只盡量簡單的說了下身份驗證涉及到的幾個核心概念,如果不明白的可以留言或等到下篇結合理解。下一篇將以使用者名稱密碼+cookie的身份驗證方式來詳細梳理下流