【asp.net core 系列】15 自定义Identity
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);
}
暂且看一下几个使用字符类型的构造函数参数:
- 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的类型。
value 存放Claim的值,通常情况下不对这个值进行约束
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的发布者,默认值是:
ClaimsIdentity.DefaultIssuer
该值允许在构造函数是传NULL,一旦传NULL,则自动认为是ClaimsIdentity.DefaultIssuer
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以达到我们的目的。
更多内容烦请关注我的博客《高先生小屋》
【asp.net core 系列】15 自定义Identity的更多相关文章
- asp.net core 系列 15 中间件
一.概述 中间件(也叫中间件组件)是一种装配到应用管道以处理请求和响应的软件. 每个组件:(1)选择是否将请求传递到管道中的下一个组件;(2)可以在管道中的下一个组件之前和之后执行工作. 请求委托用于 ...
- 【asp.net core 系列】13 Identity 身份验证入门
0. 前言 通过前两篇我们实现了如何在Service层如何访问数据,以及如何运用简单的加密算法对数据加密.这一篇我们将探索如何实现asp.net core的身份验证. 1. 身份验证 asp.net ...
- 【目录】asp.net core系列篇
随笔分类 - asp.net core系列篇 asp.net core系列 68 Filter管道过滤器 摘要: 一.概述 本篇详细了解一下asp.net core filters,filter叫&q ...
- asp.net core 系列 16 Web主机 IWebHostBuilder
一.概述 在asp.net core中,Host主机负责应用程序启动和生存期管理.host主机包括Web 主机(IWebHostBuilder)和通用主机(IHostBuilder).Web 主机是适 ...
- Asp.net Core 系列之--5.认证、授权与自定义权限的实现
ChuanGoing 2019-11-24 asp.net core系列已经来到了第五篇,通过之前的基础介绍,我们了解了事件订阅/发布的eventbus整个流程,初探dapper ORM实现,并且简单 ...
- asp.net core系列 52 Identity 其它关注点
一.登录分析 在使用identity身份验证登录时,在login中调用的方法是: var result = await _signInManager.PasswordSignInAsync(Input ...
- asp.net core系列 30 EF管理数据库架构--必备知识 迁移
一.管理数据库架构概述 EF Core 提供两种主要方法来保持 EF Core 模型和数据库架构同步.一是以 EF Core 模型为基准,二是以数据库为基准. (1)如果希望以 EF Core 模型为 ...
- WPF中的常用布局 栈的实现 一个关于素数的神奇性质 C# defualt关键字默认值用法 接口通俗理解 C# Json序列化和反序列化 ASP.NET CORE系列【五】webapi整理以及RESTful风格化
WPF中的常用布局 一 写在开头1.1 写在开头微软是一家伟大的公司.评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好,应该抛弃对微软和微软的技术的偏见. 1.2 本文内容本文主要内容 ...
- asp.net core系列 40 Web 应用MVC 介绍与详细示例
一. MVC介绍 MVC架构模式有助于实现关注点分离.视图和控制器均依赖于模型. 但是,模型既不依赖于视图,也不依赖于控制器. 这是分离的一个关键优势. 这种分离允许模型独立于可视化展示进行构建和测试 ...
随机推荐
- Java中的堆和栈
Java中的堆和栈 栈内存 存放基本数据类型和引用变量 堆内存 存放运行时创建的对象 一般来说,通过new关键字创建出来的对象都放在堆内存中 由于JVM是基于堆栈的虚拟机,而每个Java程序都运行在一 ...
- eclipse中testNG的两种安装方式
今天给大家带来两种关于testNG中的安装方式:1.在线安装(本人亲测有效!!!)2.离线安装 一.在线安装testNG插件的步骤: 1.给大家提供一个testNG在线的安装的地址:http://dl ...
- 阻塞队列一——java中的阻塞队列
目录 阻塞队列简介:介绍阻塞队列的特性与应用场景 java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列 BlockQueue中方法:介绍阻塞队列的API接口 阻塞队列的实现原理:具体的例子 ...
- Flink 如何分流数据
场景 分流方式 如何分流 使用Filter分流 使用Split分流 使用Side Output分流 场景 获取流数据的时候,通常需要根据所需把流拆分出其他多个流,根据不同的流再去作相应的处理. 举个例 ...
- usb串口的作用以及JLINK
usb串口的作用 (1)可以当串口使用 (2)如果usb串口连接到STM32的串口1(stm32ISP下载只能是串口1),可以用串口下载程序 (3)因为要连接到usb,可以用来供电 JLINK JLI ...
- 将反向传播讲解的深入透彻的神一样的文章(numpy实现人工神经网络)
为了完成机器学习课的项目,规定不许调tensorflow,pytorch这些包.可是要手工实现一个可训练的神经网络是非常困难的一件事,难点无他,就在于反向传播的实现.这不,我在网上发现了这篇文章.怎么 ...
- 几种颜色模型(颜色空间):HSV CMYK RGB
RGB和CMY颜色模型都是面向硬件的,而HSV(Hue Saturation Value)颜色模型是面向用户的. HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. ...
- 【译】Announcing Entity Framework Core 5.0 Preview 5
今天我们宣布EF Core 5.0发布第五个预览版. 1 先决条件 EF Core 5.0 的预览版要求 .NET Standard 2.1.这意味着: EF Core 5.0 在 .NET Cor ...
- SpringMVC整合mybaitis
目录 一.新建一个基于Maven的Web项目 二.创建数据库与表 三.添加依赖包 四.新建POJO实体层 五.新建MyBatis SQL映射层 六.完成Spring整合MyBatis配置 七.创建服务 ...
- Java工程中各种带有O的对象分类笔记
在Java工程里面,我们总会碰到各种不同的带有O的对象, 对于一个小白来说,经常会混淆这些对象的使用场景,所以在这里mark一下,让自己的代码更加规范,但这个也是Java被诟病的地方,不同的业务需要给 ...