在 上篇文章 中讲了关于 Identity 须要了解的单词以及相对应的几个知识点,而且知道了Identity处在整个登入流程中的位置,本篇主要是在 .NET 整个认证系统中比较重要的一个环节,就是 认证(Authentication),由于想要把 Identity 讲清楚,是绕不过 Authentication 的。javascript
在以前写过一篇关于 ASP.NET Core 中间件的文章,里面有一部分(怎么样自定义本身的中间件
)是具体关于认证系统的一个具体使用,有兴趣的朋友能够看一下这篇文章。html
其实 Identity 也是认证系统的一个具体使用,你们必定要把 Authentication 和 Identity 看成是两个东西,一旦混淆,你就容易陷入进去。java
下面就来讲一下 ASP.NET Core 中的认证系统是怎么样一回事。不要怕,其实很简单,全是干货~编程
你们应该还记得在上一篇中的奥巴马先生吧,他如今不住在华盛顿了,他到中国来旅游了,如今住在北京,这几天据说西湖风景不错,因而在 12306 定了一张北京到杭州的高铁票。取到票以后,他向咱们展现了一下:浏览器
今天是11.11号,奥巴马很开心,缘由你懂的。快到出发的时间了,因而,拿着票走到了火车站检票口,刚把身份证和火车票递给检票员。“cut”,导演喊了一声。尼玛原来是在拍电影呢~
导演说:奥巴马,你演的太烂了,别演了,你来演检票员吧,让旁边小李来演要出行路由的奥巴马吧。奥巴马不情愿的说了一声:“好吧,但愿小李可以受的了你”。cookie
“action”,导演又喊了一声,故事开始了~async
奥巴马当了检票员之后,特别高兴,由于他有权利了呀,他能够控制别人能不能上车了,说不定还能偷偷放几我的进去捞点外快呢。ide
得知了他能干什么之后,他以为检票员这个名字简直太 low 了,很快,他就有了一个新的高大上的名字,叫:认证管理员(AuthenticationManager),并且,他以为他本身应该处在整个核心位置,为何呢?你想一想看,那么庞大的一套铁路载人系统,能不能有收入有钱赚,全靠他给不给放人进去,若是一我的都不放进去,另外那一大帮人只能去喝西北风了。函数
到这里,聪明的同窗可能已经知道奥巴马把他本身放在怎么样一个核心位置了。对,他把本身放到了 HttpContext 里面。怎么样? 够核心吧。学习
这里延伸第一个知识点:AuthenticationManager 所处的位置
有同窗在上面的截图里面发现了 public abstract ClaimsPrincipal User { get; set; }
, 这不就是咱们上一篇中讲到的 “ 证件当事人 ” ,如今小李扮演的那个角色么? 对,这个 User
就是本文中的小李,被你提早发现他躲着这里了,嘿嘿。
还有一个知识点,就是 AuthenticationScheme
,什么意思呢? 且看
奥巴马敢把本身放在这么核心的位置也是有他的能力的,怎么讲呢? 好比说在检票的时候,别人递过来一张身份证和一张火车票,那怎么样验证这两个证件是合法的呢? 如下就是奥巴马提出的针对两种证件的验证方案:
方案一、针对身份证的验证,能够查看其本人是否和身份证头像是否一致,年龄是否符合当事人具体年龄。
方案二、针对火车票的验证,能够看车次,时间是否符合发车目标,另外能够看车票上的身份号码是否和身份证一致。
其中,这每一种方案,就对应一个 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:我的以为这两个不该该放在这里,由于并不属于认证的职责,也不属于协议规定的内容。可是这两个方法确实须要抽象,应该单独抽取一个接口存放,至于为何这样作,或许是由于如下缘由:
一、对登入登出的抽象是和认证紧密结合的,大多数状况下认证资料的保存是须要在SignIn进行的,好比 Cookies Authentication 中间件就在SignIn方法里面作了Cookie的保存。
二、 AuthenticationManager 这个对象是处在 HttpContext
上下文里面的,本着面向抽象和封装的原则,放到其里面是合适的,这样可以很方便的用户对其调用。
关于 AuthenticationManager 已经介绍完了,是否是很简单呢?
有些同窗可能会问了,若是 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
就能够了。
对 IAuthenticationHandler 的初步实现,封装了 AuthenticationHandler 这个抽象类,把具体的核心功能都交给下游去实现了,下面的CookieAuthentication 中间件核心类 CookieAuthenticationHandler 就是继承自AuthenticationHandler, 知道这么多就够了。
故事还要继续,奥巴马在接到小李递来的身份证和火车票以后,首先拿着火车票在一个二维码机器上扫描了一下,而后又拿着身份证在一个机器上刷了一下,通过核查,发现都没有问题。因而拿起印章在上面盖了一个 “ 验讫 ”。
这中间都发生了什么呢?
首先,在二维码扫描的过程,这个过程二维码机器会解析你火车票上的二维码,若是发现解析失败,会直接响应认证失败。也就是你别想进站了。
若是解析成功,就会获得你这个票据中的信息了,而后拿到你票据里面的的当事人信息进行验证是否被列为了铁路局黑名单中。
若是验证经过,则会给你颁发一个识别码,把符合你身份的一个识别码写入到你的火车票中和检票员旁边的电脑系统中,即 “ 验讫 ”。
话说这个验讫有点高级,它会向你的火车票芯片中写入一些信息,那么都写入些什么信息呢? 一、奥巴马我的的信息。二、验证途中的一些上下信息。三、使用的验证方案。
知道了,这些以后,那么就很容易实现这个验证方法了,对吧? 如下是 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)); }
咱们故事继续……
奥巴马检票完成以后,把票就交给了小李,小李拿到票以后,导演又喊了一声:“ 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); }
不想深刻了解的能够忽略这部份内容:
在 HandleSignInAsync 这个函数的源码中,其中有一个很巧妙的设计, 就是await Options.Events.SignedIn(signedInContext);
这样一句代码,干什么用的呢? 并且先后一共调用了两次,有同窗知道是为何吗? 我准备在下一篇中给出答案。
还记得前面 HttpContext 中的ClaimsPrincipal User
吗? 就是小李临时顶替的那个角色,如今有值了,他就是是奥巴马了。
奥巴马在座位上坐好以后,通过6个小时的路程就从北京到杭州了,不得不佩服中国高铁的速度呀,在欣赏晚西湖的风景后,奥巴马给咱们传来了一张照片:
至此,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 的关系你没看出来?。。。。真的想知道? 看下一篇吧。
顺便给本身打个广告:成都地区有推荐工做的么?有意联系右上角。
若是以为本篇博客对您有帮助的话,感谢您的【推荐】,若是你对 .NET Core 感兴趣能够关注我,我会按期在博客分享关于 .NET Core 的学习心得。
本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html
做者博客:Savorboard