在以前的文章中简单介绍了一下asp.net core中的Identity,这篇文章将继续针对Identity进行进一步的展开。c#
在《【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); }
暂且看一下几个使用字符类型的构造函数参数:函数
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的类型。ui
value 存放Claim的值,一般状况下不对这个值进行约束this
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一致。xml
这是从构造函数以及相关文档中获取到的。对象
关于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; } }
其中
这是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以达到咱们的目的。
更多内容烦请关注个人博客《高先生小屋》