在以前的文章中簡單介紹了一下asp.net core中的Identity,這篇文章將繼續針對Identity進行進一步的展開。app
在《【asp.net core 系列】13 Identity 身份驗證入門》一文中,咱們大概瞭解瞭如何使用Identity,以及如何保存一些信息以便後續的驗證。這裏咱們將深刻討論一下如何給Identity添加更多的信息。asp.net
咱們知道在給Identity添加數據的時候,須要添加一個Claim對象。咱們先回顧一下Claim的信息,Claim的屬性大多隻提供了公開的get訪問器,因此這個類的重點在於構造方法:ide
public class Claim
{
// 基礎的
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);
}
暫且看一下幾個使用字符類型的構造函數參數:函數
type Claim的類型,支持自定義,但asp.net core 提供了一個基礎的類型定義:ui
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";
}
這個類裏定義了大多數狀況下會用到的Claims的類型。this
value 存放Claim的值,一般狀況下不對這個值進行約束spa
valueType 表示 value的類型,取值範圍參考類:.net
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的發佈者,默認值是:ClaimsIdentity.DefaultIssuer
該值容許在構造函數傳NULL,一旦傳NULL,則自動認爲是ClaimsIdentity.DefaultIssuer
code
originalIssuer Claim的原發布人,若是不給值,則默認與issuer一致。orm
這是從構造函數以及相關文檔中獲取到的。
關於ClaimTypes裏我只貼了兩個,緣由是這兩個值在Claim中是兩個必不可少的值。根據屬性名就能看出來,一個是設置用戶的名稱,一個是設置用戶的角色。
那麼,繼續探索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);
}
幾個基本屬性都是從構造函數中獲取的,這裏就不作過多的介紹了。不過值得注意的一點是,Properties這個屬性的值獲取是須要使用
public Claim(BinaryReader reader, ClaimsIdentity? subject)
這個構造方法才能夠有效的對其進行賦值,因此這個屬性並無太多值得關注的地方。
介紹完了Claim類以後,咱們繼續看一下Identity相關的經常使用類:
public class ClaimsIdentity : IIdentity;
經過這個類的聲明,咱們能夠看出它實現了接口:
public interface IIdentity
{
string? AuthenticationType { get; }
bool IsAuthenticated { get; }
string? Name { get; }
}
其中
AuthenticationType 表示驗證類型
IsAuthenticated 表示是否驗證經過
Name 存放的用戶名
這是Identity裏最關鍵的三個屬性,貫穿着整個Identity體系。咱們繼續看一下ClaimsIdentity的幾個關鍵點:
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);
}
對於ClaimsIdentity而言,其核心內容是Claim實例。咱們一般須要構造Claim對象,在Claim對象中添加咱們想添加的值,而後裝入ClaimIdentity中。這裏有一個值須要額外注意一下:AuthenticationType 表示驗證類型,值並無額外要求,不過對於使用Cookie做爲信息保存的話,須要設置值爲:
CookieAuthenticationDefaults.AuthenticationScheme
這時候,咱們已經得到了一個Identity對象,在asp.net core 中 Identity體系還有最後一個關鍵類:
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);
}
這個類提供了幾個方法用來存儲Identity,這個類在IPrincipal基礎上以Identity爲基礎數據。這一點能夠經過構造函數和它提供的一些方法能夠確認。
在第一小節中,我簡單介紹了一下如何利用Claim和ClaimsIdentity以及ClaimsPrincipal這三個類來存儲用戶信息以及咱們想要的數據。這裏咱們看一下如何經過Principal讀取信息,以及簡單剖析一下背後的邏輯。
咱們使用HttpContext的擴展方法:
public static Task SignInAsync(this HttpContext context, ClaimsPrincipal principal);
將咱們設置的principal數據保存,所保存的地方取決於咱們在Startup.cs中的設置。在該系列中,咱們啓用了Cookie,因此這個信息會以Cookie的形式保存。
在控制器內部時,Controller類爲咱們提供了一個屬性:
public ClaimsPrincipal User { get; }
經過這個屬性能夠反向獲取到咱們保存的Principal實例。
接下來,讓咱們反向解析出Principal裏面的數據:
public interface IPrincipal
{
IIdentity? Identity { get; }
bool IsInRole(string role);
}
IPrincipal提供了兩個基礎數據和方法,一個是獲取一個Identity對象,一個是判斷是不是某個角色。
在ClaimPrincipal中,Identity屬性的默認取值邏輯是:
if (identities == null)
{
throw new ArgumentNullException(nameof(identities));
}
foreach (ClaimsIdentity identity in identities)
{
if (identity != null)
{
return identity;
}
}
return null;
也就是獲取Principal中第一個不爲Null的Identity對象,這個取值邏輯能夠經過下面的屬性進行修改:
public static Func<IEnumerable<ClaimsIdentity>, ClaimsIdentity?> PrimaryIdentitySelector { get; set; }
在Principal中,一般會存放一至多個Identity對象,每一個 Identity對象有一至多個Claim對象。當有Claim對象的Type 值與Identity對象的:
public string RoleClaimType { get; }
值一致時,就會被認爲該Claim裏面存放着角色信息,這時候會經過傳入的role值與Claim的Value進行比較。
比較的方法是Identity的實例方法HasClaim:
public virtual bool HasClaim(string type, string value);
若是初始化Identity時,沒有手動設置roleType參數,那麼這個參數取值就是:
public const string DefaultRoleClaimType = ClaimTypes.Role;
一般狀況下,不會單獨設置roleType。
這個屬性並非ClaimPrincipal的,而是ClaimIdentity的。一般在asp.net core 中會使用這個屬性判斷訪問者是否完成了身份校驗。這個屬性的判斷邏輯也很簡單:
public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(AuthenticationType); }
}
也就是說,在Identity中指定了AuthenticationType就會認爲完成了身份校驗。
一般的使用方式:
User.Identity.IsAuthenticated
經過以上調用鏈進行數據調用。
與IsAuthenticatedy同樣,這個屬性也是ClaimIdentity的。與IsInRole的判斷依據相似,這個屬性會獲取Identity中存放的Claim集合中第一個RoleType爲ClaimType.Name的Claim,而後取值。
因此,在實現登陸的時候,若是想要可以經過:
User.Identity.Name
獲取一個用戶名信息或者其餘名稱信息的話,則須要設置一個Type等於:
public const string DefaultNameClaimType = ClaimTypes.Name;
的Claim實例對象。
在Principal體系中,最重要也是最基礎的數據就是Claim對象。對於ClaimPrincipal對象來講,裏面必然會存放多個Claim對象。那麼,咱們就須要有操做Claim對象的方法:
public virtual IEnumerable<Claim> Claims { get; }
經過這個方法能夠得到ClaimPrincipal裏全部的Claim對象,這是一個迭代器對象。
public virtual IEnumerable<Claim> FindAll(Predicate<Claim> match);
經過一個選擇器篩選出符合條件的Claim集合。
public virtual IEnumerable<Claim> FindAll(string type);
查詢全部符合類型的Claim對象。
public virtual Claim FindFirst(string type);
查找第一個Type值與指定值相同的Claim對象。
public virtual bool HasClaim(Predicate<Claim> match);
查詢是否存在符合條件的Claim對象。
public virtual bool HasClaim(string type, string value);
查詢是否有Type和Value屬性均等於指定值的Claim對象。
這些方法都是ClaimPrincipal裏的,相對應的ClaimIdentity裏也提供了相似的方法這裏就不作介紹了。
這一章介紹瞭如何利用Claim進行用戶信息保存,以及常規的一些使用邏輯。下一章,咱們將繼續探索如何利用咱們本身設置的Identity以達到咱們的目的。