前言
在 ASP.NET Core 中,仍然沿用了 ASP.NET裡面的 Identity 組件庫,負責對使用者的身份進行認證,總體來說的話,沒有MVC 5 裡面那麼複雜,因為在MVC 5裡面引入了OWIN的東西,所以很多初學者在學習來很費勁,對於 Identity 都是一頭霧水,包括我也是,曾經在學 identity 這個東西前後花了一個多月來搞懂裡面的原理。所以大部分開發人員對於 Identity 並沒有愛,也並沒有使用它,會覺得被綁架。
值得慶幸的是,在 ASP.NET Core 中,由於對模組的抽象化逐漸清晰,以及中介軟體的使用,這使得 Identity 的學習和使用路線變得更加平易近人,下面就讓我們一起來看看吧。
Getting Started
在開始之前,讓我們先忘記它和Entity Framework
的關係,也忘記它和Authentication
的關係,我們先學習幾個英語單詞。
有這麼幾個“單詞”你可能需要弄明白:
# 1: Claims
大家應該都知道身份證長什麼樣子的,如下:
其中,姓名:奧巴馬;性別:男;民族:肯雅;出生:1961.08.04,等等這些身份資訊,可以看出都是一個一個的索引值對,那如果我們想在程式中存這些東西,怎麼樣來設計呢?對,你可能想到了使用一個字典進行儲存,一個Key,一個Value剛好滿足需求。但是Key,Value的話感覺不太友好,不太物件導向,所以如果我們做成一個對象的話,是不是更好一些呢?最起碼你可以用vs的智能提示了吧,我們修改一下,改成下面這樣:
//我給對象取一個名字叫`Claim`你沒有意見吧public class Claim{ public string ClaimType { get; set; } public string ClaimValue { get; set; }}
ClaimType 就是Key,ClaimValue就代表一個Value。這樣的話,剛好可以儲存一個索引值對。這時候姓名:奧巴馬
是不是可以存進去了。
微軟的人很貼心,給我們準備了一些預設的ClaimType
呢?很多常用的都在裡面呢,一起看看吧:
這裡延伸第一個知識點:ClaimTypes
為了閱讀體驗,我只放了一部分哦。可以看到有什麼Name,Email,Gender,MobilePhone等常用的都已經有了,其他的還有很多。細心的讀者可能注意了,它的命名空間是System.Security.Claims
,那就說明這個東西是.net 架構的一部分,嗯,我們暫時只需要知道這麼多就OK了。
Claim 介紹完畢,是不是很簡單,其他地方怎麼翻譯我不管,在本篇文章裡面,它叫 “證件單元”。
# 2: ClaimsIdentity
在有了“證件單元”之後,我們就用它可以製造一張身份證了,那麼應該怎麼樣製造呢?有些同學可能已經想到了,對,就是建立一個對象,然後在建構函式裡面把身份證單元傳輸進去,然後就得到一張身份證了。我們給這張身份證取一個英文名字叫 “ClaimsIdentity”,這個名字看起來還蠻符合的,既有 Claims 表示其組成部分,又有表示其用途的 Identity(身份),很滿意的一個名字。
實際上,在現實生活中,我們的身份證有一部分資訊是隱藏的,有一部分是可以直接看到的。比如新一代的身份證裡面儲存了你的指紋資訊你是看不到的,這些都儲存在身份證裡面的晶片中,那能看到的比如姓名啊,年齡啊等。我們在設計一個對象的時候也是一樣,需要暴露出來一些東西,那這裡我們的 ClaimsIdentity 就暴露出來一個 Name,Lable等。
我們造的身份證(ClaimsIdentity)還有一個重要的屬性就是類型(AuthenticationType),等等,AuthenticationType是什麼東西?看起來有點眼熟的樣子。我們知道我們自己的身份證是幹嘛的吧,就是用來證明我們的身份的,在你證明身份出示它的時候,其實它有很多種形式載體的,什麼意思呢?比如你可以直接拿出實體形式的身份證,那也可以是紙張形式的複印件,也可以是電子形式的電子碼等等,這個時候就需要有一個能夠表示其存在形式的類型欄位,對,這個AuthenticationType就是幹這個事情的。
然後我們在給我們的身份證添加一些潤色,讓其看起來好看,比如提供一些方法添加 Claims 的,刪除 Claims的,寫到二進位流裡面的啊等等,最終我們的身份證對象看起來基本上是這樣了:
public class ClaimsIdentity{ public ClaimsIdentity(IEnumerable<Claim> claims){} //名字這麼重要,當然不能讓別人隨便改啊,所以我不許 set,除了我兒子跟我姓,所以是 virtual 的 public virtual string Name { get; } public string Label { get; set; } //這是我的證件類型,也很重要,同樣不許 set public virtual string AuthenticationType { get; } public virtual void AddClaim(Claim claim); public virtual void RemoveClaim(Claim claim); public virtual void FindClaim(Claim claim);}
嗯,到這裡,我們的身份證看起來似乎很完美了,但是從物件導向的角度來說好像還少了點什麼東西? 對~,還是抽象,我們需要抽象出來一個介面來進行一些約束,約束什麼呢?既然作為一個證件,那麼肯定會涉及到這幾個屬性資訊:
1、名字。2、類型。3、證件是否合法。
反應到介面裡面的話就是如下,我們給介面取個名字叫:“身份(IIdentity)”:
這裡延伸第二個知識點:IIdentity介面。
// 定義證件對象的準系統。public interface IIdentity{ //證件名稱 string Name { get; } // 用於標識證件的載體類型。 string AuthenticationType { get; } //是否是合法的證件。 bool IsAuthenticated { get; }}
所以我們的 ClaimsIdentity 最終看起來定義就是這樣的了:
public class ClaimsIdentity : IIdentity{ //......}
ClaimsIdentity 介紹完畢,是不是發現也很簡單,其他地方怎麼翻譯我不管,在本篇文章裡面,它叫 “身份證”。
# 3: ClaimsPrincipal
有了身份證,我們就能證明我就是我了,有些時候一個人有很多張身份證,你猜這個人是幹嘛的? 對,不是黃牛就是詐騙犯。
但是,有些時候一個人還有其他很多種身份,你猜這個人是幹嘛的?這就很正常了對不對,比如你可以同時是一名教師,母親,商人。如果你想證明你同時有這幾種身份的時候,你可能需要出示教師證,你孩子的出生證,法人代表的營業執照證。
在程式中,一個身份證不僅僅代表你這個人了,而是代表一個身份,是證明你自己的主要身份哦。如果一個人還有其他很多種身份,這個時候就需要有一個東西(載體)來攜帶著這些證件了對吧?OK,我們給需要攜帶證件的這個對象取一個貼切點的名字,叫“證件當事人(ClaimsPrincipal)”吧。
以下是 Principal 這個單詞在詞典給出的解釋,我用它你應該沒意見吧:
principal ['prɪnsəpl] adj. 主要的;資本的n. 首長;校長;資本;當事人
這個時候可能有同學會問了,是不是應該叫ClaimsIdentityPrincipal
比較好呢?嗯,我也覺得應該叫 ClaimsIdentityPrincipal 可能更好一點,或許微軟的人偷懶了,簡寫成了ClaimsPrincipal
。
知道其功能後,代碼就很好寫了,和上面ClaimsIdentity一樣的套路:
public class ClaimsPrincipal { //把擁有的證件都給當事人 public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities){} //當事人的主身份呢 public virtual IIdentity Identity { get; } public virtual IEnumerable<ClaimsIdentity> Identities { get; } public virtual void AddIdentity(ClaimsIdentity identity); //為什麼沒有RemoveIdentity , 留給大家思考吧?}
當時人看起來也幾乎完美了,但是我們還需要對其抽象一下,抽象哪些東西呢? 作為一個當事人,你應該有一個主身份吧,就是你的身份證咯,可能你還會用到角色(角色後面會詳細介紹,這裡你知道有這麼個東西就行了)。
這裡延伸第三個知識點:IPrincipal 介面。
public interface IPrincipal{ //身份 IIdentity Identity { get; } //在否屬於某個角色 bool IsInRole(string role);}
然後,我們的 證件當事人 看起來應該是這樣的:
public class ClaimsPrincipal : IPrincipal { //...}
ClaimsPrincipal 介紹完了,也很簡單吧? 其他地方怎麼翻譯我不管,在本篇文章裡面,它叫 “證件當事人”。
想在,我們已經知道了 “證件單元(Claims)” , “身份證(ClaimsIdentity)” , “證件當事人(ClaimsPrincipal)”,並且整理清楚了他們之間的邏輯關係,趁熱打鐵,下面這個圖是一個identity登入部分的不完全,虛線圈出來的部分應該可以看懂了吧:
可以看出,首先我們在app這邊有一些證件單元,然後調用ClaimsIdentity
把證件單元初始化為一個身份證,然後再把身份證交給證件當事人由其保管。
才把 Getting Started 寫完,發現已經這麼長了,所以打算寫成一個系列了,可能3 - 4篇吧。
總結
好了,本篇就先介紹到這裡,在本篇部落格中,我們學會了幾個英文單詞,並且知道了這些英文單詞在程式中是扮演這怎麼樣一個對象。並且根據圖我們知道了這些對象在整個認證系統種處在怎麼樣一個位置。 我發現如果想把 identity 講清楚僅僅靠這一篇部落格是不夠的,下一篇我們將對.NET Authentication
中介軟體進行抽絲剝繭,直到掌握.NET的整個認證系統後,我們再來看一下 Identiy 到底和 Entity Framework 有著怎樣的愛恨情仇。
這僅僅是一個開始,大家如果覺得本篇部落格對您有協助的話,感謝您的【推薦】,如果你對 .NET Core 感興趣可以關注我,我會定期在部落格分享關於 .NET Core 的學習心得。