在 上篇文章 中講了關於 Identity 須要瞭解的單詞以及相對應的幾個知識點,而且知道了Identity處在整個登入流程中的位置,本篇主要是在 .NET 整個認證系統中比較重要的一個環節,就是 認證(Authentication),由於想要把 Identity 講清楚,是繞不過 Authentication 的。javascript
在以前寫過一篇關於 ASP.NET Core 中間件的文章,裏面有一部分(怎麼樣自定義本身的中間件
)是具體關於認證系統的一個具體使用,有興趣的朋友能夠看一下這篇文章。html
其實 Identity 也是認證系統的一個具體使用,你們必定要把 Authentication 和 Identity 看成是兩個東西,一旦混淆,你就容易陷入進去。java
下面就來講一下 ASP.NET Core 中的認證系統是怎麼樣一回事。不要怕,其實很簡單,全是乾貨~編程
你們應該還記得在上一篇中的奧巴馬先生吧,他如今不住在華盛頓了,他到中國來旅遊了,如今住在北京,這幾天據說西湖風景不錯,因而在 12306 定了一張北京到杭州的高鐵票。取到票以後,他向咱們展現了一下:瀏覽器
今天是11.11號,奧巴馬很開心,緣由你懂的。快到出發的時間了,因而,拿着票走到了火車站檢票口,剛把身份證和火車票遞給檢票員。「cut」,導演喊了一聲。尼瑪原來是在拍電影呢~
導演說:奧巴馬,你演的太爛了,別演了,你來演檢票員吧,讓旁邊小李來演要出行路由的奧巴馬吧。奧巴馬不情願的說了一聲:「好吧,但願小李可以受的了你」。cookie
「action」,導演又喊了一聲,故事開始了~async
奧巴馬當了檢票員之後,特別高興,由於他有權利了呀,他能夠控制別人能不能上車了,說不定還能偷偷放幾我的進去撈點外快呢。ide
得知了他能幹什麼之後,他以爲檢票員這個名字簡直太 low 了,很快,他就有了一個新的高大上的名字,叫:認證管理員(AuthenticationManager),並且,他以爲他本身應該處在整個核心位置,爲何呢?你想一想看,那麼龐大的一套鐵路載人系統,能不能有收入有錢賺,全靠他給不給放人進去,若是一我的都不放進去,另外那一大幫人只能去喝西北風了。函數
到這裏,聰明的同窗可能已經知道奧巴馬把他本身放在怎麼樣一個核心位置了。對,他把本身放到了 HttpContext 裏面。怎麼樣? 夠核心吧。學習
這裏延伸第一個知識點:AuthenticationManager 所處的位置
有同窗在上面的截圖裏面發現了 public abstract ClaimsPrincipal User { get; set; }
, 這不就是咱們上一篇中講到的 「 證件當事人 」 ,如今小李扮演的那個角色麼? 對,這個 User
就是本文中的小李,被你提早發現他躲着這裏了,嘿嘿。
還有一個知識點,就是 AuthenticationScheme
,什麼意思呢? 且看
奧巴馬敢把本身放在這麼核心的位置也是有他的能力的,怎麼講呢? 好比說在檢票的時候,別人遞過來一張身份證和一張火車票,那怎麼樣驗證這兩個證件是合法的呢? 如下就是奧巴馬提出的針對兩種證件的驗證方案:
方案一、針對身份證的驗證,能夠查看其本人是否和身份證頭像是否一致,年齡是否符合當事人具體年齡。
方案二、針對火車票的驗證,能夠看車次,時間是否符合發車目標,另外能夠看車票上的身份號碼是否和身份證一致。
其中,這每一種方案,就對應一個 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:我的以爲這兩個不該該放在這裏,由於並不屬於認證的職責,也不屬於協議規定的內容。可是這兩個方法確實須要抽象,應該單獨抽取一個接口存放,至於爲何這樣作,或許是由於如下緣由:
一、對登入登出的抽象是和認證緊密結合的,大多數狀況下認證資料的保存是須要在SignIn進行的,好比 Cookies Authentication 中間件就在SignIn方法裏面作了Cookie的保存。
二、 AuthenticationManager 這個對象是處在 HttpContext
上下文裏面的,本着面向抽象和封裝的原則,放到其裏面是合適的,這樣可以很方便的用戶對其調用。
關於 AuthenticationManager 已經介紹完了,是否是很簡單呢?
有些同窗可能會問了,若是 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
就能夠了。
對 IAuthenticationHandler 的初步實現,封裝了 AuthenticationHandler 這個抽象類,把具體的核心功能都交給下游去實現了,下面的CookieAuthentication 中間件核心類 CookieAuthenticationHandler 就是繼承自AuthenticationHandler, 知道這麼多就夠了。
故事還要繼續,奧巴馬在接到小李遞來的身份證和火車票以後,首先拿着火車票在一個二維碼機器上掃描了一下,而後又拿着身份證在一個機器上刷了一下,通過覈查,發現都沒有問題。因而拿起印章在上面蓋了一個 「 驗訖 」。
這中間都發生了什麼呢?
首先,在二維碼掃描的過程,這個過程二維碼機器會解析你火車票上的二維碼,若是發現解析失敗,會直接響應認證失敗。也就是你別想進站了。
若是解析成功,就會獲得你這個票據中的信息了,而後拿到你票據裏面的的當事人信息進行驗證是否被列爲了鐵路局黑名單中。
若是驗證經過,則會給你頒發一個識別碼,把符合你身份的一個識別碼寫入到你的火車票中和檢票員旁邊的電腦系統中,即 「 驗訖 」。
話說這個驗訖有點高級,它會向你的火車票芯片中寫入一些信息,那麼都寫入些什麼信息呢? 一、奧巴馬我的的信息。二、驗證途中的一些上下信息。三、使用的驗證方案。
知道了,這些以後,那麼就很容易實現這個驗證方法了,對吧? 如下是 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)); }
咱們故事繼續……
奧巴馬檢票完成以後,把票就交給了小李,小李拿到票以後,導演又喊了一聲:「 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 的學習心得。
本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html
做者博客:Savorboard