0. 前言

在之前的文章中简单介绍了一下asp.net core中的Identity,这篇文章将继续针对Identity进行进一步的展开。

1. 给Identity添加额外的信息

在《【asp.net core 系列】13 Identity 身份验证入门》一文中,我们大概了解了如何使用Identity,以及如何保存一些信息以便后续的验证。这里我们将深入讨论一下如何给Identity添加更多的信息。

我们知道在给Identity添加数据的时候,需要添加一个Claim对象。我们先回顾一下Claim的信息,Claim的属性大多只提供了公开的get访问器,所以这个类的重点在于构造方法:

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);
}

暂且看一下几个使用字符类型的构造函数参数:

  1. type Claim的类型,支持自定义,但asp.net core 提供了一个基础的类型定义:
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的类型。

  1. value 存放Claim的值,通常情况下不对这个值进行约束

  2. 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";
    }
  3. issuer 用来存放 Claim的发布者,默认值是:ClaimsIdentity.DefaultIssuer 该值允许在构造函数是传NULL,一旦传NULL,则自动认为是ClaimsIdentity.DefaultIssuer

  4. originalIssuer Claim的原发布人,如果不给值,则默认与issuer一致。

这是从构造函数以及相关文档中获取到的。

关于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为基础数据。这一点可以通过构造函数和它提供的一些方法可以确认。

2. 读取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对象,一个是判断是否是某个角色。

2.1 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; }

2.2 IsInRole

在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。

2.3 IsAuthenticated 判断是否登录

这个属性并不是ClaimPrincipal的,而是ClaimIdentity的。通常在asp.net core 中会使用这个属性判断访问者是否完成了身份校验。这个属性的判断逻辑也很简单:

public virtual bool IsAuthenticated
{
get { return !string.IsNullOrEmpty(AuthenticationType); }
}

也就是说,在Identity中指定了AuthenticationType就会认为完成了身份校验。

通常的使用方式:

User.Identity.IsAuthenticated

通过以上调用链进行数据调用。

2.4 Name

与IsAuthenticatedy一样,这个属性也是ClaimIdentity的。与IsInRole的判断依据类似,这个属性会获取Identity中存放的Claim集合中第一个RoleType为ClaimType.Name的Claim,然后取值。

所以,在实现登录的时候,如果想要能够通过:

User.Identity.Name

获取一个用户名信息或者其他名称信息的话,则需要设置一个Type等于:

public const string DefaultNameClaimType = ClaimTypes.Name;

的Claim实例对象。

2.5 获取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里也提供了类似的方法这里就不做介绍了。

3. 总结

这一章介绍了如何利用Claim进行用户信息保存,以及常规的一些使用逻辑。下一章,我们将继续探索如何利用我们自己设置的Identity以达到我们的目的。

更多内容烦请关注我的博客《高先生小屋》

最新文章

  1. MFC listcontrol导出excel表格
  2. 开放-封闭原则(OCP)开-闭原则 和 依赖倒转原则,单一职责原则
  3. Droid4x快照还原
  4. SQL——用户定义函数
  5. Fast-cgi cgi nginx php-fpm 的关系 (转
  6. 关于lib,dll,.a,.so,静态库和动态库的解释说明
  7. Python基础:1.数据类型(空、布尔类型、整型、长整型、浮点型、字符串)
  8. Mysql锁机制和事务控制
  9. 数字信号处理Day1自制电子音乐
  10. (Problem 4)Largest palindrome product
  11. dreamweaver代码提示失效
  12. 单片机驱动AT24C02存储芯片
  13. docker简单介绍----Dockerfile命令
  14. linux shell 发送qq邮件失败
  15. 疯狂Workflow讲义&mdash;&mdash;基于Activiti的工作流应用开 PDF 下载
  16. IDEA中导入多个包自动合并为星号
  17. 关于变量参数的传递,python让人蛋痛的地方
  18. 30分钟了解Shiro与Springboot的多Realm基础配置
  19. Numpy 函数总结 (不断更新)
  20. Error: Couldn&#39;t find preset &quot;env&quot; relative to directory &quot;/Users/user/ethereumjs-vm&quot;

热门文章

  1. 基于EntityFramework 6 Code First实现动态建库,分库,数据库自动迁移
  2. apt update 提示 Release file for http://&hellip; is not valid yet (invalid for another d..)
  3. Idea创建Scala的Maven项目
  4. Arduino + RFID 读取 IC 卡 Arduino uno中获得RFID的UID 并通过串口转发RFID卡号
  5. php 判断设备是手机还是平板还是pc
  6. belongsTo和hasOne的区别
  7. cb02a_c++_数据结构_顺序容器_STL_list类_双向链表
  8. Css教程玉女心经版本
  9. Lens —— 最炫酷的 Kubernetes 桌面客户端
  10. Spark中自定义累加器