0. 前言
在之前的文章中簡(jiǎn)單介紹了一下asp.net core中的Identity狭魂,這篇文章將繼續(xù)針對(duì)Identity進(jìn)行進(jìn)一步的展開。
1. 給Identity添加額外的信息
在《【asp.net core 系列】13 Identity 身份驗(yàn)證入門》一文中蛛枚,我們大概了解了如何使用Identity,以及如何保存一些信息以便后續(xù)的驗(yàn)證脸哀。這里我們將深入討論一下如何給Identity添加更多的信息蹦浦。
我們知道在給Identity添加數(shù)據(jù)的時(shí)候,需要添加一個(gè)Claim對(duì)象撞蜂。我們先回顧一下Claim的信息盲镶,Claim的屬性大多只提供了公開的get訪問器,所以這個(gè)類的重點(diǎn)在于構(gòu)造方法:
public class Claim
{
// 基礎(chǔ)的
public Claim(string type, string value);
public Claim(string type, string value, string valueType);
public Claim(string type, string value, string valueType, string issuer);
public Claim(string type, string value, string valueType, string issuer, string originalIssuer);
//
public Claim(BinaryReader reader);
public Claim(BinaryReader reader, ClaimsIdentity subject);
}
暫且看一下幾個(gè)使用字符類型的構(gòu)造函數(shù)參數(shù):
- type Claim的類型蝌诡,支持自定義溉贿,但asp.net core 提供了一個(gè)基礎(chǔ)的類型定義:
public static class ClaimTypes
{
// 隱藏其他屬性
public const string Name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name";
public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
}
? 這個(gè)類里定義了大多數(shù)情況下會(huì)用到的Claims的類型。
value 存放Claim的值浦旱,通常情況下不對(duì)這個(gè)值進(jìn)行約束
-
valueType 表示 value的類型宇色,取值范圍參考類:
public static class ClaimValueTypes { public const string Base64Binary = "http://www.w3.org/2001/XMLSchema#base64Binary"; public const string UpnName = "http://schemas.xmlsoap.org/claims/UPN"; public const string UpnName = "http://schemas.xmlsoap.org/claims/UPN"; public const string UInteger32 = "http://www.w3.org/2001/XMLSchema#uinteger32"; public const string Time = "http://www.w3.org/2001/XMLSchema#time"; public const string String = "http://www.w3.org/2001/XMLSchema#string"; public const string Sid = "http://www.w3.org/2001/XMLSchema#sid"; public const string RsaKeyValue = "http://www.w3.org/2000/09/xmldsig#RSAKeyValue"; public const string Rsa = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/rsa"; public const string Rfc822Name = "urn:oasis:names:tc:xacml:1.0:data-type:rfc822Name"; public const string KeyInfo = "http://www.w3.org/2000/09/xmldsig#KeyInfo"; public const string Integer64 = "http://www.w3.org/2001/XMLSchema#integer64"; public const string X500Name = "urn:oasis:names:tc:xacml:1.0:data-type:x500Name"; public const string Integer32 = "http://www.w3.org/2001/XMLSchema#integer32"; public const string HexBinary = "http://www.w3.org/2001/XMLSchema#hexBinary"; public const string Fqbn = "http://www.w3.org/2001/XMLSchema#fqbn"; public const string Email = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"; public const string DsaKeyValue = "http://www.w3.org/2000/09/xmldsig#DSAKeyValue"; public const string Double = "http://www.w3.org/2001/XMLSchema#double"; public const string DnsName = "http://schemas.xmlsoap.org/claims/dns"; public const string DaytimeDuration = "http://www.w3.org/TR/2002/WD-xquery-operators-20020816#dayTimeDuration"; public const string DateTime = "http://www.w3.org/2001/XMLSchema#dateTime"; public const string Date = "http://www.w3.org/2001/XMLSchema#date"; public const string Boolean = "http://www.w3.org/2001/XMLSchema#boolean"; public const string Base64Octet = "http://www.w3.org/2001/XMLSchema#base64Octet"; public const string Integer = "http://www.w3.org/2001/XMLSchema#integer"; public const string YearMonthDuration = "http://www.w3.org/TR/2002/WD-xquery-operators-20020816#yearMonthDuration"; }
issuer 用來存放 Claim的發(fā)布者,默認(rèn)值是:
ClaimsIdentity.DefaultIssuer
該值允許在構(gòu)造函數(shù)是傳NULL,一旦傳NULL代兵,則自動(dòng)認(rèn)為是ClaimsIdentity.DefaultIssuer
originalIssuer Claim的原發(fā)布人尼酿,如果不給值,則默認(rèn)與issuer一致植影。
這是從構(gòu)造函數(shù)以及相關(guān)文檔中獲取到的裳擎。
關(guān)于ClaimTypes里我只貼了兩個(gè),原因是這兩個(gè)值在Claim中是兩個(gè)必不可少的值思币。根據(jù)屬性名就能看出來鹿响,一個(gè)是設(shè)置用戶的名稱,一個(gè)是設(shè)置用戶的角色谷饿。
那么惶我,繼續(xù)探索Claim里的屬性和方法:
public class Claim
{
public string Type { get; }
public ClaimsIdentity Subject { get; }
public IDictionary<string, string> Properties { get; }
public string OriginalIssuer { get; }
public string Issuer { get; }
public string ValueType { get; }
public string Value { get; }
public virtual Claim Clone();
public virtual Claim Clone(ClaimsIdentity identity);
public virtual void WriteTo(BinaryWriter writer);
}
幾個(gè)基本屬性都是從構(gòu)造函數(shù)中獲取的,這里就不做過多的介紹了博投。不過值得注意的一點(diǎn)是绸贡,Properties這個(gè)屬性的值獲取是需要使用
public Claim(BinaryReader reader, ClaimsIdentity? subject)
這個(gè)構(gòu)造方法才可以有效的對(duì)其進(jìn)行賦值,所以這個(gè)屬性并沒有太多值得關(guān)注的地方毅哗。
介紹完了Claim類之后听怕,我們繼續(xù)看一下Identity相關(guān)的常用類:
public class ClaimsIdentity : IIdentity;
通過這個(gè)類的聲明,我們可以看出它實(shí)現(xiàn)了接口:
public interface IIdentity
{
string? AuthenticationType { get; }
bool IsAuthenticated { get; }
string? Name { get; }
}
其中
- AuthenticationType 表示驗(yàn)證類型
- IsAuthenticated 表示是否驗(yàn)證通過
- Name 存放的用戶名
這是Identity里最關(guān)鍵的三個(gè)屬性虑绵,貫穿著整個(gè)Identity體系尿瞭。我們繼續(xù)看一下ClaimsIdentity的幾個(gè)關(guān)鍵點(diǎn):
public class ClaimsIdentity : IIdentity
{
public ClaimsIdentity(string authenticationType);
public ClaimsIdentity(IIdentity identity);
public ClaimsIdentity(IEnumerable<Claim> claims);
public ClaimsIdentity(IEnumerable<Claim> claims, string authenticationType);
public ClaimsIdentity(IIdentity identity, IEnumerable<Claim> claims);
public virtual void AddClaim(Claim claim);
public virtual void AddClaims(IEnumerable<Claim> claims);
}
對(duì)于ClaimsIdentity而言,其核心內(nèi)容是Claim實(shí)例翅睛。我們通常需要構(gòu)造Claim對(duì)象声搁,在Claim對(duì)象中添加我們想添加的值,然后裝入ClaimIdentity中捕发。這里有一個(gè)值需要額外注意一下:AuthenticationType 表示驗(yàn)證類型疏旨,值并沒有額外要求,不過對(duì)于使用Cookie作為信息保存的話爬骤,需要設(shè)置值為:
CookieAuthenticationDefaults.AuthenticationScheme
這時(shí)候充石,我們已經(jīng)獲得了一個(gè)Identity對(duì)象,在asp.net core 中 Identity體系還有最后一個(gè)關(guān)鍵類:
public class ClaimsPrincipal : IPrincipal
{
public ClaimsPrincipal();
public ClaimsPrincipal(IIdentity identity);
public ClaimsPrincipal(IPrincipal principal);
public virtual void AddIdentities(IEnumerable<ClaimsIdentity> identities);
public virtual void AddIdentity(ClaimsIdentity identity);
}
這個(gè)類提供了幾個(gè)方法用來存儲(chǔ)Identity霞玄,這個(gè)類在IPrincipal基礎(chǔ)上以Identity為基礎(chǔ)數(shù)據(jù)骤铃。這一點(diǎn)可以通過構(gòu)造函數(shù)和它提供的一些方法可以確認(rèn)。
2. 讀取Identity的信息
在第一小節(jié)中坷剧,我簡(jiǎn)單介紹了一下如何利用Claim和ClaimsIdentity以及ClaimsPrincipal這三個(gè)類來存儲(chǔ)用戶信息以及我們想要的數(shù)據(jù)惰爬。這里我們看一下如何通過Principal讀取信息,以及簡(jiǎn)單剖析一下背后的邏輯惫企。
我們使用HttpContext的擴(kuò)展方法:
public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal);
將我們?cè)O(shè)置的principal數(shù)據(jù)保存撕瞧,所保存的地方取決于我們?cè)赟tartup.cs中的設(shè)置陵叽。在該系列中,我們啟用了Cookie丛版,所以這個(gè)信息會(huì)以Cookie的形式保存巩掺。
在控制器內(nèi)部時(shí),Controller類為我們提供了一個(gè)屬性:
public ClaimsPrincipal User { get; }
通過這個(gè)屬性可以反向獲取到我們保存的Principal實(shí)例页畦。
接下來胖替,讓我們反向解析出Principal里面的數(shù)據(jù):
public interface IPrincipal
{
IIdentity? Identity { get; }
bool IsInRole(string role);
}
IPrincipal提供了兩個(gè)基礎(chǔ)數(shù)據(jù)和方法,一個(gè)是獲取一個(gè)Identity對(duì)象豫缨,一個(gè)是判斷是否是某個(gè)角色独令。
2.1 Identity
在ClaimPrincipal中,Identity屬性的默認(rèn)取值邏輯是:
if (identities == null)
{
throw new ArgumentNullException(nameof(identities));
}
foreach (ClaimsIdentity identity in identities)
{
if (identity != null)
{
return identity;
}
}
return null;
也就是獲取Principal中第一個(gè)不為Null的Identity對(duì)象好芭,這個(gè)取值邏輯可以通過下面的屬性進(jìn)行修改:
public static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> PrimaryIdentitySelector { get; set; }
2.2 IsInRole
在Principal中燃箭,通常會(huì)存放一至多個(gè)Identity對(duì)象,每個(gè) Identity對(duì)象有一至多個(gè)Claim對(duì)象舍败。當(dāng)有Claim對(duì)象的Type 值與Identity對(duì)象的:
public string RoleClaimType { get; }
值一致時(shí)招狸,就會(huì)被認(rèn)為該Claim里面存放著角色信息,這時(shí)候會(huì)通過傳入的role值與Claim的Value進(jìn)行比較瓤湘。
比較的方法是Identity的實(shí)例方法HasClaim:
public virtual bool HasClaim(string type, string value);
如果初始化Identity時(shí)瓢颅,沒有手動(dòng)設(shè)置roleType參數(shù)恩尾,那么這個(gè)參數(shù)取值就是:
public const string DefaultRoleClaimType = ClaimTypes.Role;
通常情況下弛说,不會(huì)單獨(dú)設(shè)置roleType。
2.3 IsAuthenticated 判斷是否登錄
這個(gè)屬性并不是ClaimPrincipal的翰意,而是ClaimIdentity的木人。通常在asp.net core 中會(huì)使用這個(gè)屬性判斷訪問者是否完成了身份校驗(yàn)。這個(gè)屬性的判斷邏輯也很簡(jiǎn)單:
public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(AuthenticationType); }
}
也就是說冀偶,在Identity中指定了AuthenticationType就會(huì)認(rèn)為完成了身份校驗(yàn)醒第。
通常的使用方式:
User.Identity.IsAuthenticated
通過以上調(diào)用鏈進(jìn)行數(shù)據(jù)調(diào)用。
2.4 Name
與IsAuthenticatedy一樣进鸠,這個(gè)屬性也是ClaimIdentity的稠曼。與IsInRole的判斷依據(jù)類似,這個(gè)屬性會(huì)獲取Identity中存放的Claim集合中第一個(gè)RoleType為ClaimType.Name的Claim客年,然后取值霞幅。
所以,在實(shí)現(xiàn)登錄的時(shí)候量瓜,如果想要能夠通過:
User.Identity.Name
獲取一個(gè)用戶名信息或者其他名稱信息的話司恳,則需要設(shè)置一個(gè)Type等于:
public const string DefaultNameClaimType = ClaimTypes.Name;
的Claim實(shí)例對(duì)象。
2.5 獲取Claim
在Principal體系中绍傲,最重要也是最基礎(chǔ)的數(shù)據(jù)就是Claim對(duì)象扔傅。對(duì)于ClaimPrincipal對(duì)象來說耍共,里面必然會(huì)存放多個(gè)Claim對(duì)象。那么猎塞,我們就需要有操作Claim對(duì)象的方法:
public virtual IEnumerable<Claim> Claims { get; }
通過這個(gè)方法可以獲得ClaimPrincipal里所有的Claim對(duì)象试读,這是一個(gè)迭代器對(duì)象。
public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);
通過一個(gè)選擇器篩選出符合條件的Claim集合荠耽。
public virtual IEnumerable<Claim> FindAll(string type);
查詢所有符合類型的Claim對(duì)象鹏往。
public virtual Claim FindFirst(string type);
查找第一個(gè)Type值與指定值相同的Claim對(duì)象。
public virtual bool HasClaim(Predicate<Claim> match);
查詢是否存在符合條件的Claim對(duì)象骇塘。
public virtual bool HasClaim(string type, string value);
查詢是否有Type和Value屬性均等于指定值的Claim對(duì)象伊履。
這些方法都是ClaimPrincipal里的,相對(duì)應(yīng)的ClaimIdentity里也提供了類似的方法這里就不做介紹了款违。
3. 總結(jié)
這一章介紹了如何利用Claim進(jìn)行用戶信息保存唐瀑,以及常規(guī)的一些使用邏輯。下一章插爹,我們將繼續(xù)探索如何利用我們自己設(shè)置的Identity以達(dá)到我們的目的哄辣。
更多內(nèi)容煩請(qǐng)關(guān)注我的博客《高先生小屋》