ASP.NET Core 之 Identity 入門(二)
前言
在 上篇文章 中講了關於 Identity 需要了解的單詞以及相對應的幾個知識點,並且知道了Identity處在整個登入流程中的位置,本篇主要是在 .NET 整個認證系統中比較重要的一個環節,就是 認證(Authentication),因為想要把 Identity 講清楚,是繞不過 Authentication 的。
在之前寫過一篇關於 ASP.NET Core 中介軟體的文章,裡面有一部分(怎麼樣自定義自己的中介軟體
)是具體關於認證系統的一個具體使用,有興趣的朋友可以看一下這篇文章。
其實 Identity 也是認證系統的一個具體使用,大家一定要把 Authentication 和 Identity 當作是兩個東西,一旦混淆,你就容易陷入進去。
下面就來說一下 ASP.NET Core 中的認證系統是怎麼樣一回事。不要怕,其實很簡單,全是乾貨~
Getting Started
大家應該還記得在上一篇中的奧巴馬先生吧,他現在不住在華盛頓了,他到中國來旅遊了,現在住在北京,這幾天聽說西湖風景不錯,於是在 12306 定了一張北京到杭州的高鐵票。取到票之後,他向我們展示了一下:
今天是11.11號,奧巴馬很開心,原因你懂的。快到出發的時間了,於是,拿著票走到了火車站檢票口,剛把身份證和火車票遞給檢票員。“cut”,導演喊了一聲。尼瑪原來是在拍電影呢~
導演說:奧巴馬,你演的太爛了,別演了,你來演檢票員吧,讓旁邊小李來演要出行路由的奧巴馬吧。奧巴馬不情願的說了一聲:“好吧,希望小李能夠受的了你”。
“action”,導演又喊了一聲,故事開始了~
AuthenticationManager
奧巴馬當了檢票員以後,特別高興,因為他有權利了呀,他可以控制別人能不能上車了,說不定還能偷偷放幾個人進去撈點外快呢。
得知了他能幹什麼以後,他覺得檢票員這個名字簡直太 low 了,很快,他就有了一個新的高大上的名字,叫:認證管理員(AuthenticationManager),而且,他覺得他自己應該處在整個核心位置,為什麼呢?你想想看,那麼龐大的一套鐵路載人系統,能不能有收入有錢賺,全靠他給不給放人進去,如果一個人都不放進去,另外那一大幫人只能去喝西北風了。
到這裡,聰明的同學可能已經知道奧巴馬把他自己放在怎麼樣一個核心位置了。對,他把自己放到了 HttpContext 裡面。怎麼樣? 夠核心吧。
這裡延伸第一個知識點:AuthenticationManager 所處的位置
有同學在上面的截圖裡面發現了 public abstract ClaimsPrincipal User { get; set; }
, 這不就是我們上一篇中講到的 “ 證件當事人 ” ,現在小李扮演的那個角色麼? 對,這個 User
就是本文中的小李,被你提前發現他躲著這裡了,嘿嘿。
還有一個知識點,就是 AuthenticationScheme
,什麼意思呢? 且看
奧巴馬敢把自己放在這麼核心的位置也是有他的能力的,怎麼講呢? 比如說在檢票的時候,別人遞過來一張身份證和一張火車票,那怎麼樣驗證這兩個證件是合法的呢? 以下就是奧巴馬提出的針對兩種證件的驗證方案:
方案1、針對身份證的驗證,可以檢視其本人是否和身份證頭像是否一致,年齡是否符合當事人具體年齡。
方案2、針對火車票的驗證,可以看車次,時間是否符合發車目標,另外可以看車票上的身份號碼是否和身份證一致。
其中,這每一種方案,就對應一個 AuthenticationScheme
(驗證方案名稱),是不是明白了。
這就是第二個知識點 AuthenticationScheme 很重要。
知道了奧巴馬的職責後,就很容易的把程式碼寫出來了:
public abstract class AuthenticationManager
{
//AuthenticateContext包含了需要認證的上下文,裡面就有小李
public abstract Task AuthenticateAsync(AuthenticateContext context);
//握手
public abstract Task ChallengeAsync(string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior);
//登入
public abstract Task SignInAsync(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties);
//登出
public abstract Task SignOutAsync(string authenticationScheme, AuthenticationProperties properties);
}
奧巴馬做為一個檢票員,有一個認證方法,AuthenticateAsync()
,注意這是其一個核心功能,其他幾個都可以沒有,但是唯獨不能沒有這個功能,沒有的話他就不能稱之為一個檢票員了。
然後還有一個握手ChallengeAsync
,登入SignInAsync
和登出SignOutAsync
,下面說說筆者對這三個方法的理解吧。
ChallengeAsync:是社群協議檔案 RFC2167 定義的關於在HTTP Authentication 過程中的一種關於握手的一個過程,主要是摘要認證(digest authentication),更多資訊檢視這裡。
是不是有點專業,看不懂,沒事,有通俗版本的。 小李要進站了,這個時候小李問了一下我們的檢票員奧巴馬先生。
- 小李:“你好,檢票員,我可以進站嗎?”
- 檢票員奧巴馬:“要趕火車嗎?可以啊,請出示你的證件?”
- 小李:“好的,這是我的證件,你檢查一下?”
- 檢票員奧巴馬:“嗯,證件沒問題,進去吧”
這樣一個過程就是握手(digest-challenge)或者叫問答的一個過程,明白了 ChallengeAsync 的原理了吧? 是不是很簡單。
SignInAsync,SignOutAsync:個人覺得這兩個不應該放在這裡,因為並不屬於認證的職責,也不屬於協議規定的內容。但是這兩個方法確實需要抽象,應該單獨抽取一個介面存放,至於為什麼這樣做,或許是因為以下原因:
1、對登入登出的抽象是和認證緊密結合的,大多數情況下認證資料的儲存是需要在SignIn進行的,比如 Cookies Authentication 中介軟體就在SignIn方法裡面做了Cookie的儲存。
2、 AuthenticationManager 這個物件是處在 HttpContext
上下文裡面的,本著面向抽象和封裝的原則,放到其裡面是合適的,這樣能夠很方便的使用者對其呼叫。
關於 AuthenticationManager 已經介紹完了,是不是很簡單呢?
IAuthenticationHandler
有些同學可能會問了,如果 AuthenticationManager 不提供介面的話,只是一個抽象類的話,那如果自定義認證方法就必須繼承它,這對於開發者來說是不友好的,也違背了面向介面程式設計的理念。嗯,確實是這樣,那麼介面來了:
public interface IAuthenticationHandler
{
void GetDescriptions(DescribeSchemesContext context);
Task AuthenticateAsync(AuthenticateContext context);
Task ChallengeAsync(ChallengeContext context);
Task SignInAsync(SignInContext context);
Task SignOutAsync(SignOutContext context);
}
這個介面是在 AuthenticationManager 實現類 DefaultAuthenticationManager
中延伸出來的,所以大家不用再去看裡面的原始碼了,記住以後如果需要重寫認證相關的東西,實現IAuthenticationHandler
就可以了。
Authentication 中介軟體
對 IAuthenticationHandler 的初步實現,封裝了 AuthenticationHandler 這個抽象類,把具體的核心功能都交給下游去實現了,下面的CookieAuthentication 中介軟體核心類 CookieAuthenticationHandler 就是繼承自AuthenticationHandler, 知道這麼多就夠了。
CookieAuthentication 中介軟體
故事還要繼續,奧巴馬在接到小李遞來的身份證和火車票之後,首先拿著火車票在一個二維碼機器上掃描了一下,然後又拿著身份證在一個機器上刷了一下,經過核查,發現都沒有問題。於是拿起印章在上面蓋了一個 “ 驗訖 ”。
這中間都發生了什麼呢?
首先,在二維碼掃描的過程,這個過程二維碼機器會解析你火車票上的二維碼,如果發現解析失敗,會直接響應認證失敗。也就是你別想進站了。
如果解析成功,就會得到你這個票據中的資訊了,然後拿到你票據裡面的的當事人資訊進行驗證是否被列為了鐵路局黑名單中。
如果驗證通過,則會給你頒發一個識別碼,把符合你身份的一個識別碼寫入到你的火車票中和檢票員旁邊的電腦系統中,即 “ 驗訖 ”。
話說這個驗訖有點高階,它會向你的火車票晶片中寫入一些資訊,那麼都寫入些什麼資訊呢? 1、奧巴馬個人的資訊。2、驗證途中的一些上下資訊。3、使用的驗證方案。
知道了,這些之後,那麼就很容易實現這個驗證方法了,對吧? 以下是 CookieAuthentication 中介軟體中的核心類 CookieAuthenticationHandler 的裡面的核心方法HandleAuthenticateAsync(),同樣你可以理解為實現的 IAuthenticationHandler 介面的 AuthenticateAsync:
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// 解析二維碼
var result = await EnsureCookieTicket();
if (!result.Succeeded)
{
return result;
}
// 從二維碼中拿當事人資訊進行驗證
var context = new CookieValidatePrincipalContext(Context, result.Ticket, Options);
await Options.Events.ValidatePrincipal(context);
if (context.Principal == null)
{
return AuthenticateResult.Fail("No principal.");
}
if (context.ShouldRenew)
{
RequestRefresh(result.Ticket);
}
// 驗訖, 寫入晶片
return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
}
HandleSignInAsync
我們故事繼續……
奧巴馬檢票完成之後,把票就交給了小李,小李拿到票之後,導演又喊了一聲:“ cut ”……
怎麼又停了,小李和奧巴馬一肚子的疑惑,導演說:“ 奧巴馬呀,你檢票員演的不錯,還是繼續扮演你的本職角色吧,演好了中午盒飯給你雙份,小李,你來演檢票員吧 ”。
可以吃兩份盒飯了,奧巴馬聽後心裡還是很開心。
“action” 導演喊了一聲……
奧巴馬接過票,向著車站裡面的列車停車處走去,走到了列車門口要進去的時候,又出現了一個人,奧巴馬知道,這個人就是做車內乘客登記的(ps: 一般情況下,做乘客登記都是在列車行駛的過程中,在這裡我們假設這個做乘客登記的人比較勤快,就在車門口守著),登記完成之後就讓奧巴馬進去了。
那麼,登記這個過程中都幹了些什麼呢?
首先,登記員的手持裝置會解析火車票票裡面寫入晶片中的資訊,發現沒有問題,就開始向自己手裡面的登記本登記資訊了,主要包含車票主人資訊,過期時間,稽核人等。
這樣整個過程就是 HandleSignInAsync 的一個過程,換成程式術語就是,組裝 Cookie 登入上下文資訊,寫入到 Http 流的 header 中,也就寫入到了客戶端瀏覽器cookie。
至此,整個過程就完了,我們來看一下程式碼:
//方法裡面的流程,我只列出了核心部分,影響閱讀的全刪了
protected override async Task HandleSignInAsync(SignInContext signin)
{
// 解析晶片中的資訊
var result = await EnsureCookieTicket();
// 組織登入上下文,設定過期時間等
// 使用 data protected 加密登記本上的資訊
var cookieValue = Options.TicketDataFormat.Protect(ticket);
// 寫入到瀏覽器header
await ApplyHeaders(cookieValue);
}
不想深入瞭解的可以忽略這部分內容:
在 HandleSignInAsync 這個函式的原始碼中,其中有一個很巧妙的設計, 就是await Options.Events.SignedIn(signedInContext);
這樣一句程式碼,幹什麼用的呢? 而且前後一共呼叫了兩次,有同學知道是為什麼嗎? 我準備在下一篇中給出答案。
還記得前面 HttpContext 中的ClaimsPrincipal User
嗎? 就是小李臨時頂替的那個角色,現在有值了,他就是是奧巴馬了。
奧巴馬在座位上坐好之後,經過6個小時的路程就從北京到杭州了,不得不佩服中國高鐵的速度呀,在欣賞晚西湖的風景後,奧巴馬給我們傳來了一張照片:
至此,CookieAuthentication 中介軟體的整個工作流程已經講完了,故事也結束了。
以上,就是這兩行程式碼背後的故事:
var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "奧巴馬") }, CookieAuthenticationDefaults.AuthenticationScheme));
await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
總結
在本篇中我們知道了 AuthenticationManager,也知道了 IAuthenticationHandler 並且簡單的介紹了一下 Authentication 中介軟體和 CookieAuthentication 中介軟體,其中 CookieAuthentication 中介軟體是我們以後使用最多的一箇中間件了,本篇也對其做了一個詳細的介紹,我想通過本篇文章在以後使用的過程中應該問題不大了。
有同學可能會問了,講了這麼多認證的東西它和 Identity 有什麼關係呢? 難道我通篇都在隱藏他和 Identity 的關係你沒看出來?。。。。真的想知道? 看下一篇吧。
順便給自己打個廣告:成都地區有推薦工作的麼?有意聯絡右上角。
如果覺得本篇部落格對您有幫助的話,感謝您的【推薦】,如果你對 .NET Core 感興趣可以關注我,我會定期在部落格分享關於 .NET Core 的學習心得。
相關推薦
ASP.NET Core 之 Identity 入門(二)
前言 在 上篇文章 中講了關於 Identity 需要了解的單詞以及相對應的幾個知識點,並且知道了Identity處在整個登入流程中的位置,本篇主要是在 .NET 整個認證系統中比較重要的一個環節,就是 認證(Authentication),因為想要把 Identity 講清楚,是繞不過 Authentica
ASP.NET Core 之 Identity 入門(一)
前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET裡面的 Identity 元件庫,負責對使用者的身份進行認證,總體來說的話,沒有MVC 5 裡面那麼複雜,因為在MVC 5裡面引入了OWIN的東西,所以很多初學者在學習來很費勁,對於 Identity 都是一頭霧水,包括我也是,曾經在學 ide
ASP.NET Core 2 學習筆記(二)生命周期
RF Go 使用 HR runt block top 最大的 env 原文:ASP.NET Core 2 學習筆記(二)生命周期要了解程序的運行原理,就要先知道程序的進入點及生命周期。以往ASP.NET MVC的啟動方式,是繼承 HttpApplication 作為網站開始
asp.net core 2.1 dotnet (二)
呼叫 ET 的方式 新建 APPDbContext 這樣一個檔案: using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using Mi
.NET Core之單元測試(二):使用記憶體資料庫處理單元測試中的資料庫依賴
目錄 定義一個待測試API 測試用例 為減少篇幅,隱藏了SampleEntity和SqliteDbContext 定義一個待測試API 如下,我們定義了一個名為Sample的API,其中有一個外部依賴項Sqli
.NET Core之單元測試(一):入門
目錄 什麼是單元測試 .NET Core中的測試框架 一個最基礎的單元測試 我們再看看上面的程式碼 什麼是單元測試 單元測試是對軟體中的最小可測試單元進行檢查和驗證。對於單元測試
asp.net core 擁抱 docker 技術 (一)概覽
測試 docker 架構 swa ima 進程 基於 概念 registry 這是一個huge 坑慢慢填吧。這裏只是一個目錄 或總覽。 docker 是什麽? docker可以看做一種虛擬機技術,但沒有傳統虛擬機那麽復雜,是基於進程的虛擬,就是讓一個一個進程,認為自己處於一
ASP.NET Core 2 學習筆記(十)視圖
部分 合成 cati 分享 col script text var AC ASP.NET Core MVC中的Views是負責網頁顯示,將數據一並渲染至UI包含HTML、CSS等。並能痛過Razor語法在*.cshtml中寫渲染畫面的程序邏輯。本篇將介紹ASP.NET Co
ASP.NET Core 2 學習筆記(四)依賴註入
pub framework 三次 DDM order 包裝 差異 限制 cto 原文:ASP.NET Core 2 學習筆記(四)依賴註入ASP.NET Core使用了大量的依賴註入(Dependency Injection, DI),把控制反轉(Inversion Of
ASP.NET Core 2 學習筆記(七)路由
local quest urn AD term 執行 自動 routes code 原文:ASP.NET Core 2 學習筆記(七)路由ASP.NET Core通過路由(Routing)設定,將定義的URL規則找到相對應行為;當使用者Request的URL滿足特定規則條件
ASP.NET Core 2 學習筆記(六)MVC
方便 web redirect AR return his 架構模式 PE ofo 原文:ASP.NET Core 2 學習筆記(六)MVC ASP.NET Core MVC跟ASP.NET MVC觀念是一致的,使用上也沒有什麽太大的變化。之前的ASP.NET MVC把MV
【從零開始搭建自己的.NET Core Api框架】(二)搭建項目的整體架構
config 七層 數據 TP 暫時 整體架構 比較 架構 其他 本來打算將搭建項目架構和集成SqlSugar放在一起講的,但是感覺東西有點多,還是分成兩章吧~ 這一章講搭建項目的整體架構,這裏先把搭建完成後的最終效果放出來,然後再逐個解釋每層的作用。 可以看到這裏一
ASP.NET Core 配置跨域(CORS)
tin sha har exce pub header service 策略 uil 1.安裝程序CORS程序包 Install-Package Microsoft.AspNetCore.Mvc.Cors 一般默認都帶了此程序包的 2.配置CORS服務 在 Startu
基於ASP.NET的新聞管理系統(二)效果展示
後臺 family 技術 .net image 密碼 src 管理系 修改密碼 5. 新聞管理系統開發與實現 5.1前臺新聞頁面 主頁面 新聞欄展示新聞 搜索新聞 菜單欄鏈接新聞 後臺登錄界面 大管理員後臺管理界面 小管理員後臺管理界面 修改密
ASP.NET Core 專案配置 ( Startup )(轉載)
原文:https://www.twle.cn/l/yufei/aspnetcore/dotnet-aspnet-startup.html 由於是個人網站,怕沒了,特意複製儲存,個人覺得講的非常透徹 前面幾章節中我們已經介紹和使用過 Startup 類
ASP.NET Core AD 域登入 (轉載)
在選擇AD登入時,其實可以直接選擇 Windows 授權,不過因為有些網站需要的是LDAP獲取資訊進行授權,而非直接依賴Web Server自帶的Windows 授權功能。 當然如果使用的是Azure AD/企業賬號登入時,直接在ASP.NET Core建立專案時選擇就好了。來個ABC:1.
asp.net core 2.1 dotnet(五)EF.core Dbfirst 生成 檢視與呼叫儲存過程
1 檢視,可以直接在 Model中寫 檢視的模型 執行方式如找到一個,還沒有試過: 有關 .net core EF 的問題有一個地址: https://github.com/aspnet/EntityFra
asp.net core 2.1 dotnet(四)EF.core DBFisrt生成模型類
工具的scaffold-dbcontext(資料庫上下文腳手架)指令來生成models和context。 指令詳細介紹: Scaffold-DbContext [-Connection] <String> [-Provider] <String>
在 ASP.NET CORE 中使用 SESSION (轉載)
Session 是儲存使用者和 Web 應用的會話狀態的一種方法,ASP.NET Core 提供了一個用於管理會話狀態的中介軟體。在本文中我將會簡單介紹一下 ASP.NET Core 中的 Session 的使用方法。 安裝配置 Session nuget 新增引用 M
.net core mvc初級教程(二)
這一片是關於用引入bootstrap前端框架的教程,置於bootstrap是什麼? bootstrap是一組用於網站和網路應用程式開發的開源前端(所謂“前端”,指的是展現給終端使用者的介面。與之對應的“後端”是在伺服器上面執行的程式碼)框架,包括HTML、CSS及JavaScript的框架,