在 上篇文章 中讲了关于 Identity 需要了解的单词以及相对应的几个知识点,并且知道了Identity处在整个登入流程中的位置,本篇主要是在 .NET 整个认证系统中比较重要的一个环节,就是 认证(Authentication),因为想要把 Identity 讲清楚,是绕不过 Authentication 的。

在之前写过一篇关于 ASP.NET Core 中间件的文章,里面有一部分(怎么样自定义自己的中间件)是具体关于认证系统的一个具体使用,有兴趣的朋友可以看一下这篇文章。

其实 Identity 也是认证系统的一个具体使用,大家一定要把 Authentication 和 Identity 当作是两个东西,一旦混淆,你就容易陷入进去。

下面就来说一下 ASP.NET Core 中的认证系统是怎么样一回事。不要怕,其实很简单,全是干货~

Getting Started

大家应该还记得在上一篇中的奥巴马先生吧,他现在不住在华盛顿了,他到中国来旅游了,现在住在北京,这几天听说西湖风景不错,于是在 12306 定了一张北京到杭州的高铁票。取到票之后,他向我们展示了一下:

今天是11.11号,奥巴马很开心,原因你懂的。快到出发的时间了,于是,拿着票走到了火车站检票口,刚把身份证和火车票递给检票员。“cut”,导演喊了一声。尼玛原来是在拍电影呢~
导演说:奥巴马,你演的太烂了,别演了,你来演检票员吧,让旁边小李来演要出行路由的奥巴马吧。奥巴马不情愿的说了一声:“好吧,希望小李能够受的了你”。

“action”,导演又喊了一声,故事开始了~

AuthenticationManager

奥巴马当了检票员以后,特别高兴,因为他有权利了呀,他可以控制别人能不能上车了,说不定还能偷偷放几个人进去捞点外快呢。

得知了他能干什么以后,他觉得检票员这个名字简直太 low 了,很快,他就有了一个新的高大上的名字,叫:认证管理员(AuthenticationManager),而且,他觉得他自己应该处在整个核心位置,为什么呢?你想想看,那么庞大的一套铁路载人系统,能不能有收入有钱赚,全靠他给不给放人进去,如果一个人都不放进去,另外那一大帮人只能去喝西北风了。

到这里,聪明的同学可能已经知道奥巴马把他自己放在怎么样一个核心位置了。对,他把自己放到了 HttpContext 里面。怎么样? 够核心吧。

这里延伸第一个知识点:AuthenticationManager 所处的位置

有同学在上面的截图里面发现了 public abstract ClaimsPrincipal User { get; set; }, 这不就是我们上一篇中讲到的 “ 证件当事人 ” ,现在小李扮演的那个角色么? 对,这个 User 就是本文中的小李,被你提前发现他躲着这里了,嘿嘿。

还有一个知识点,就是 AuthenticationScheme,什么意思呢? 且看
奥巴马敢把自己放在这么核心的位置也是有他的能力的,怎么讲呢? 比如说在检票的时候,别人递过来一张身份证和一张火车票,那怎么样验证这两个证件是合法的呢? 以下就是奥巴马提出的针对两种证件的验证方案:

方案1、针对身份证的验证,可以查看其本人是否和身份证头像是否一致,年龄是否符合当事人具体年龄。
方案2、针对火车票的验证,可以看车次,时间是否符合发车目标,另外可以看车票上的身份号码是否和身份证一致。

其中,这每一种方案,就对应一个 AuthenticationScheme(验证方案名称),是不是明白了。

这就是第二个知识点 AuthenticationScheme 很重要。

知道了奥巴马的职责后,就很容易的把代码写出来了:

public abstract class AuthenticationManager
{ //AuthenticateContext包含了需要认证的上下文,里面就有小李
public abstract Task AuthenticateAsync(AuthenticateContext context); //握手
public abstract Task ChallengeAsync(string authenticationScheme, AuthenticationProperties properties, ChallengeBehavior behavior); //登入
public abstract Task SignInAsync(string authenticationScheme, ClaimsPrincipal principal, AuthenticationProperties properties); //登出
public abstract Task SignOutAsync(string authenticationScheme, AuthenticationProperties properties);
}

奥巴马做为一个检票员,有一个认证方法,AuthenticateAsync() ,注意这是其一个核心功能,其他几个都可以没有,但是唯独不能没有这个功能,没有的话他就不能称之为一个检票员了。

然后还有一个握手ChallengeAsync,登入SignInAsync和登出SignOutAsync,下面说说笔者对这三个方法的理解吧。

ChallengeAsync:是社区协议文件 RFC2167 定义的关于在HTTP Authentication 过程中的一种关于握手的一个过程,主要是摘要认证(digest authentication),更多信息查看这里。

是不是有点专业,看不懂,没事,有通俗版本的。 小李要进站了,这个时候小李问了一下我们的检票员奥巴马先生。

  • 小李:“你好,检票员,我可以进站吗?”
  • 检票员奥巴马:“要赶火车吗?可以啊,请出示你的证件?”
  • 小李:“好的,这是我的证件,你检查一下?”
  • 检票员奥巴马:“嗯,证件没问题,进去吧”

这样一个过程就是握手(digest-challenge)或者叫问答的一个过程,明白了 ChallengeAsync 的原理了吧? 是不是很简单。

SignInAsync,SignOutAsync:个人觉得这两个不应该放在这里,因为并不属于认证的职责,也不属于协议规定的内容。但是这两个方法确实需要抽象,应该单独抽取一个接口存放,至于为什么这样做,或许是因为以下原因:

1、对登入登出的抽象是和认证紧密结合的,大多数情况下认证资料的保存是需要在SignIn进行的,比如 Cookies Authentication 中间件就在SignIn方法里面做了Cookie的保存。

2、 AuthenticationManager 这个对象是处在 HttpContext
上下文里面的,本着面向抽象和封装的原则,放到其里面是合适的,这样能够很方便的用户对其调用。

关于 AuthenticationManager 已经介绍完了,是不是很简单呢?

IAuthenticationHandler

有些同学可能会问了,如果 AuthenticationManager 不提供接口的话,只是一个抽象类的话,那如果自定义认证方法就必须继承它,这对于开发者来说是不友好的,也违背了面向接口编程的理念。嗯,确实是这样,那么接口来了:

public interface IAuthenticationHandler
{
void GetDescriptions(DescribeSchemesContext context); Task AuthenticateAsync(AuthenticateContext context); Task ChallengeAsync(ChallengeContext context); Task SignInAsync(SignInContext context); Task SignOutAsync(SignOutContext context);
}

这个接口是在 AuthenticationManager 实现类 DefaultAuthenticationManager 中延伸出来的,所以大家不用再去看里面的源码了,记住以后如果需要重写认证相关的东西,实现IAuthenticationHandler就可以了。

Authentication 中间件

对 IAuthenticationHandler 的初步实现,封装了 AuthenticationHandler 这个抽象类,把具体的核心功能都交给下游去实现了,下面的CookieAuthentication 中间件核心类 CookieAuthenticationHandler 就是继承自AuthenticationHandler, 知道这么多就够了。

CookieAuthentication 中间件

故事还要继续,奥巴马在接到小李递来的身份证和火车票之后,首先拿着火车票在一个二维码机器上扫描了一下,然后又拿着身份证在一个机器上刷了一下,经过核查,发现都没有问题。于是拿起印章在上面盖了一个 “ 验讫 ”。

这中间都发生了什么呢?

首先,在二维码扫描的过程,这个过程二维码机器会解析你火车票上的二维码,如果发现解析失败,会直接响应认证失败。也就是你别想进站了。

如果解析成功,就会得到你这个票据中的信息了,然后拿到你票据里面的的当事人信息进行验证是否被列为了铁路局黑名单中。

如果验证通过,则会给你颁发一个识别码,把符合你身份的一个识别码写入到你的火车票中和检票员旁边的电脑系统中,即 “ 验讫 ”。

话说这个验讫有点高级,它会向你的火车票芯片中写入一些信息,那么都写入些什么信息呢? 1、奥巴马个人的信息。2、验证途中的一些上下信息。3、使用的验证方案。

知道了,这些之后,那么就很容易实现这个验证方法了,对吧? 以下是 CookieAuthentication 中间件中的核心类 CookieAuthenticationHandler 的里面的核心方法HandleAuthenticateAsync(),同样你可以理解为实现的 IAuthenticationHandler 接口的 AuthenticateAsync:

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// 解析二维码
var result = await EnsureCookieTicket();
if (!result.Succeeded)
{
return result;
} // 从二维码中拿当事人信息进行验证
var context = new CookieValidatePrincipalContext(Context, result.Ticket, Options);
await Options.Events.ValidatePrincipal(context); if (context.Principal == null)
{
return AuthenticateResult.Fail("No principal.");
} if (context.ShouldRenew)
{
RequestRefresh(result.Ticket);
} // 验讫, 写入芯片
return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
}

HandleSignInAsync

我们故事继续……

奥巴马检票完成之后,把票就交给了小李,小李拿到票之后,导演又喊了一声:“ cut ”……

怎么又停了,小李和奥巴马一肚子的疑惑,导演说:“ 奥巴马呀,你检票员演的不错,还是继续扮演你的本职角色吧,演好了中午盒饭给你双份,小李,你来演检票员吧 ”。
可以吃两份盒饭了,奥巴马听后心里还是很开心。

“action” 导演喊了一声……

奥巴马接过票,向着车站里面的列车停车处走去,走到了列车门口要进去的时候,又出现了一个人,奥巴马知道,这个人就是做车内乘客登记的(ps: 一般情况下,做乘客登记都是在列车行驶的过程中,在这里我们假设这个做乘客登记的人比较勤快,就在车门口守着),登记完成之后就让奥巴马进去了。

那么,登记这个过程中都干了些什么呢?

首先,登记员的手持设备会解析火车票票里面写入芯片中的信息,发现没有问题,就开始向自己手里面的登记本登记信息了,主要包含车票主人信息,过期时间,审核人等。

这样整个过程就是 HandleSignInAsync 的一个过程,换成程序术语就是,组装 Cookie 登入上下文信息,写入到 Http 流的 header 中,也就写入到了客户端浏览器cookie。

至此,整个过程就完了,我们来看一下代码:


//方法里面的流程,我只列出了核心部分,影响阅读的全删了
protected override async Task HandleSignInAsync(SignInContext signin)
{
// 解析芯片中的信息
var result = await EnsureCookieTicket(); // 组织登入上下文,设置过期时间等
// 使用 data protected 加密登记本上的信息
var cookieValue = Options.TicketDataFormat.Protect(ticket); // 写入到浏览器header
await ApplyHeaders(cookieValue);
}

还记得前面 HttpContext 中的ClaimsPrincipal User吗? 就是小李临时顶替的那个角色,现在有值了,他就是是奥巴马了。

至此,CookieAuthentication 中间件的整个工作流程已经讲完了,故事也结束了。

以上,就是这两行代码背后的故事:

var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "奥巴马") }, CookieAuthenticationDefaults.AuthenticationScheme));
await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);

总结

在本篇中我们知道了 AuthenticationManager,也知道了 IAuthenticationHandler 并且简单的介绍了一下 Authentication 中间件和 CookieAuthentication 中间件,其中 CookieAuthentication 中间件是我们以后使用最多的一个中间件了,本篇也对其做了一个详细的介绍,我想通过本篇文章在以后使用的过程中应该问题不大了。

有同学可能会问了,讲了这么多认证的东西它和 Identity 有什么关系呢? 难道我通篇都在隐藏他和 Identity 的关系你没看出来?。。。。真的想知道? 看下一篇吧。


本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html
作者博客:Savorboard
欢迎转载,请在明显位置给出出处及链接

Identity入门2:AuthenticationManager【转】的更多相关文章

  1. ASP.NET Core 之 Identity 入门(三)

    前言 在上一篇文章中,我们学习了 CookieAuthentication 中间件,本篇的话主要看一下 Identity 本身. 最早2005年 ASP.NET 2.0 的时候开始, Web 应用程序 ...

  2. [转]ASP.NET Core 之 Identity 入门(三)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html 前言 在上一篇文章中,我们学习了 CookieAuthentica ...

  3. 【ASP.NET Identity系列教程(一)】ASP.NET Identity入门

    注:本文是[ASP.NET Identity系列教程]的第一篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...

  4. ASP.NET Identity系列教程-2【Identity入门】

    https://www.cnblogs.com/r01cn/p/5177708.html13 Identity入门 Identity is a new API from Microsoft to ma ...

  5. ASP.NET Core 之 Identity 入门(二)

    前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以及相对应的几个知识点,并且知道了Identity处在整个登入流程中的位置,本篇主要是在 .NET 整个认证系统中比较重要的一个环节,就 ...

  6. [转]ASP.NET Core 之 Identity 入门(二)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html 前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以 ...

  7. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  8. ASP.NET Identity入门系列教程(一) 初识Identity

    摘要 通过本文你将了解ASP.NET身份验证机制,表单认证的基本流程,ASP.NET Membership的一些弊端以及ASP.NET Identity的主要优势. 目录 身份验证(Authentic ...

  9. [转]ASP.NET Core 之 Identity 入门(一)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html 前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里 ...

随机推荐

  1. 游戏设计艺术 第2版 (Jesse Schell 著)

    第1章 太初之时,有设计师 (已看) 第2章 设计师创造体验 第3章 体验发生于场景 第4章 体验从游戏中诞生 第5章 游戏由元素构成 第6章 元素支撑起主题 第7章 游戏始于一个创意 第8章 游戏通 ...

  2. day_0

    Day01 - 初识Python - Python简介 - Python的历史 / Python的优缺点 / Python的应用领域 - 搭建编程环境 - Windows环境 / Linux环境 / ...

  3. eclipse 离线安装SVN插件(支持eclipse201909)

      1.情景展示 重装eclipse后,按照网上的在线安装方法,SVN始终安装失败,之前的离线SVN包也不能用. 2.解决方案 SVN离线包下载地址:http://subclipse.tigris.o ...

  4. spring boot 不占用端口方式启动

    随着微服务架构的流行,想要启动一个微服务架构项目就要开启好多端口,有时候一台机器上部署的项目多的时候,端口资源就比较紧张了,其实有的微服务组件仅仅只是提供RPC服务,可以不用占用web启动的端口,此时 ...

  5. 【转】pywinauto教程

    一.环境安装 1.命令行安装方法 pip install pywinauto==0.6.7 2.手动安装方法 安装包下载链接:pyWin32: python调用windows api的库https:/ ...

  6. 构建Shiny应用

    构建Shiny应用 1.什么是Shiny? Shiny是一个R的应用包,帮助用户构建可交互的web应用.它可以结合HTML和CSS代码,以及R 语言的运算能力. 2.下载R Shiny 下载R包 in ...

  7. SpringDataRedis入门Demo

    步骤: 约定>配置>代码 在pom.xml中导入依赖(redis和jedis以及其他所需的依赖) > 配置相关配置文件(redis-config.properties 和applic ...

  8. A query was run and no Result Maps were found for the Mapped Statement

    mybatis测试方法报错: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exec ...

  9. 2019-11-27-WPF-全屏透明窗口

    原文:2019-11-27-WPF-全屏透明窗口 title author date CreateTime categories WPF 全屏透明窗口 lindexi 2019-11-27 09:22 ...

  10. 禁止直接通过IP访问--->nginx

    在nginx.conf 中添加 server{ listen 80 default_server; return 501; } 注: nginx加载include是按顺序,如果是文件夹,就是文件顺序, ...