ASP.NET Core 中的那些认证中间件及一些重要知识点

前言

在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(ASP.NET Core 之 Identity 入门(一),ASP.NET Core 之 Identity 入门(二),ASP.NET Core 之 Identity 入门(三)奠定一下基础。

有关于 Authentication 的知识太广,所以本篇介绍几个在 ASP.NET Core 认证中会使用到的中间件,还有Authentication的一些零碎知识点,这些知识点对于 ASP.NET 认证体系的理解至关重要。

在 Github 中 ASP.NET Core 关于 Authentication 的实现有以下几个包,那么这几个包的功能分别是干什么用的呢?我们一一看一下。

  • Microsoft.AspNetCore.Authentication

  • Microsoft.AspNetCore.Authentication.Cookies

  • Microsoft.AspNetCore.Authentication.OAuth

  • Microsoft.AspNetCore.Authentication.OpenIdConnect

  • Microsoft.AspNetCore.Authentication.JwtBearer

Microsoft.AspNetCore.Authentication

Authentication 在 ASP.NET Core 中,对于 Authentication(认证) 的抽象实现,此中间件用来处理或者提供管道中的 HttpContext 里面的 AuthenticationManager 相关功能抽象实现。

HttpContext 中的 User 相关信息同样在此中间件中被初始化。

对于开发人员只需要了解此中间件中的这几个对象即可:

  • AuthenticationOptions 对象主要是用来提供认证相关中间件配置项,后面的 OpenIdConnect,OAuth,Cookie 等均是继承于此。

  • AuthenticationHandler 对请求流程中(Pre-Request)中相关认证部分提供的一个抽象处理类,同样后面的其他几个中间件均是继承于此。

在 AuthenticationHandler 中, 有几个比较重要的方法:

  • HandleAuthenticateAsync :处理认证流程中的一个核心方法,这个方法返回 AuthenticateResult来标记是否认证成功以及返回认证过后的票据(AuthenticationTicket)。

  • HandleUnauthorizedAsync:可以重写此方法用来处理相应401未授权等问题,修改头,或者跳转等。

  • HandleSignInAsync:对齐 HttpContext AuthenticationManager 中的 SignInAsync

  • HandleSignOutAsync:对齐 HttpContext AuthenticationManager 中的 SignOutAsync

  • HandleForbiddenAsync:对齐 HttpContext AuthenticationManager 中的 ForbidAsync,用来处理禁用等结果

以上关于 AuthenticationHandler 我列出来的这些方法都是非常容易理解的,我们在继承Authentication实现我们自己的一个中间件的时候只需要重写上面的一个或者多个方法即可。

还有一个 RemoteAuthenticationHandler 它也是继承AuthenticationHandler,主要是针对于远程调用提供出来的一个抽象,什么意思呢?因为很多时候我们的认证是基于OAuth的,也就是说用户的状态信息是存储到Http Header 里面每次进行往来的,而不是cookie等,所以在这个类里面了一个HandleRemoteAuthenticateAsync的函数。

Microsoft.AspNetCore.Authentication.Cookies

Cookies 认证是 ASP.NET Core Identity 默认使用的身份认证方式,那么这个中间件主要是干什么的呢

我们知道,在 ASP.NET Core 中已经没有了 Forms 认证,取而代之的是一个叫 “个人用户账户” 的一个东西,如下图,你在新建一个ASP.ENT Core Web 应用程序的时候就会发现它,它实际上就是 Identity。那么Forms认证去哪里了呢?对,就是换了个名字叫 Identity。

在此中间件中,主要是针对于Forms认证的一个实现,也就是说它通过Cookie把用户的个人身份信息通过加密的票据存储的Cookie中去,如果看过我之前Identity系列文章的话,那么应该知道用户的个人身份信息就是 ClaimsIdentity 相关的东西。

这个中间件引用了Authentication,CookieAuthenticationHandler 类继承了 AuthenticationHandler 并重写了 HandleAuthenticateAsync,HandleSignInAsync,HandleSignOutAsyncHandleForbiddenAsync,HandleUnauthorizedAsync 等方法,就是上一节中列出来的几个方法。

我们主要看一下核心方法 HandleAuthenticateAsync 在 Cookie 中间件怎么实现的:

protected override async Task<AuthenticateResult> HandleAuthenticateAsync(){    //读取并解密从浏览器获取的Cookievar result = await EnsureCookieTicket();    if (!result.Succeeded){        return result;}     // 使用上一步结果构造 CookieValidatePrincipalContext 对象// 这个对象是一个包装类,里面装着 ClaimsPrincipal、AuthenticationPropertiesvar context = new CookieValidatePrincipalContext(Context, result.Ticket, Options);    // 默认是空的实现,可以重写来验证 CookieValidatePrincipalContext 是否有异常await Options.Events.ValidatePrincipal(context);    if (context.Principal == null){        return AuthenticateResult.Fail("No principal.");}    // 表示是否需要刷新Cookieif (context.ShouldRenew){RequestRefresh(result.Ticket);}    return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
}

总结一下就是解密Http请求中的Cookie信息,然后验证Cookie是否合法,然后提取Cookie中的信息返回结果。

还有一个方法就是 HandleSignInAsync ,根据名字可以看出主要是处理登入相关操作的,在这个方法里面主要是根据Claims信息生成加入过后的票据,同时会向票据中写入过期时间,是否持久化等信息。 是否持久化的意思就是用户在登陆界面是否勾选了 “记住我” 这个操作。

Microsoft.AspNetCore.Authentication.OAuth

OAuth 是针对于 OAuth 2.0 标准实现的一个客户端程序,记住是客户端,它不具备发放Token或者 Client_id ,Code 等的功能,它的作用是帮你简化对OAuth2.0服务端程序的调用。 它对应 OAuth 2.0 标准中的 “获取Access_Token” 这一步骤,如果对腾讯开放平台QQ授权比较了解的话,就是对应 “使用Authorization_Code获取Access_Token” 这一步骤。

点击 这里 查看图片详情。

OAuth 实现的具体细节就不一一介绍了。

Microsoft.AspNetCore.Authentication.OpenIdConnect

获取 OpenId 是OAuth 授权中的一个步骤,OpenId 它是具体的一个Token Key,不要把他理解成一种授权方式或者和OAuth不同的另外一种东西,他们是一体的。

代码上就不详细说了,和上面的都差不多。主要说一下它们之间的区别或者叫联系。

OAuth 它主要是针对于授权(Authorization),而OpenID主要是针对于认证(Authentication),他们之间是互补的。

那什么叫授权呢? 比如小明是使用我们网站的一个用户,他现在要在另外一个网站使用在我们网站注册的账号,那授权就是代表小明在另外一个网站能够做什么东西? 比如能够查看资料,头像,相册等等,授权会给用户发放一个叫 Access_Token 的东西。

而认证关注的这个用户是谁,它是用来证明用户东西。比如小明要访问它的相册,那我们网站就需要小明提供一个叫OpenId的一个东西,我们只认这个OpenId。那小明从哪里得到它的这个OpenId呢,对,就是使用上一步的Access_Token 来换取这 个 OpenId ,以后访问的时候不认 Access_Token ,只认识OpenId这个东西。

一般情况下,OpenId 是需要客户端进行持久化的,那么对应在 ASP.NET Core Identity 中,就是存储在 UsersLogin 表里面的 ProviderKey 字段,懂了吧,懂了给个推荐呗

Microsoft.AspNetCore.Authentication.JwtBearer

JwtBearer 这个中间件是依赖于上一步的 OpenIdConnect 中间件的,看到了吧,其实这几个中间件是环环嵌套的。

可能很多同学听说过 Jwt,但是大多数人都有一个误区,认为JWT是一种认证方式,经常在QQ群里面听过 前面一个同学在问 实际开发中前后端分离的时候安全怎么做的?,下面一个人回答使用JWT。

其实JWT 它不是一种认证方式,也不是一种认证的技术,它是一个规范,一个标准。

Jwt(Json Web Token)的官网是 https://jwt.io,下面是对JWT的一个说明

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
JSON Web Tokens(JWT) 是一个开放的行业标准( RFC 7519),用于在双方之间传递安全的Claims。

JWT 在身份认证中的应用场景:

在身份认证场景下,一旦用户完成了登陆,在接下来的每个请求中包含JWT,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销非常小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。

好了,不过多的说了。 我们还是接着看一下 JwtBearer 中间件,同样它重写了 HandleAuthenticateAsync 方法。

大致步骤如下:

  1. 读取 Http Request Header 中的 Authorization 信息

  2. 读取 Authorization 值里面的 Bearer 信息

  3. 验证 Bearer 是否合法,会得到一个 ClaimsPrincipal

  4. 使用 ClaimsPrincipal 构建一个 Ticket(票据)

  5. 调用 Options.Events.TokenValidated(context),用户可以重写此方法验证Token合法性

  6. 返回验证成功

其他的知识点

这几个中间件对会有对应的 Options 配置项,在这些配置项中,都会有 AuthenticationSchemeAutomaticAuthenticateAutomaticChallenge 这几个属性,那这几个东西都是干嘛的呢?

AuthenticationScheme

我在 《ASP.NET Core 之 Identity 入门(二)》 一文中提到过这个知识点,当时说很重要,这里可以看到了吧,每一种验证中间件都会使用到这个东西,我比较偏向于把这个翻译成 “认证方案”。

我们知道,在 MVC 程序中一般通过在 Controller 或者 Action 上 打标记(Attribute)的方式进行授权,最典型的就是新建一个项目的时候里面的AccountController。

[Authorize]public class AccountController : Controller{
}

在 Authorize 这个 Attribute 中,有一个属性叫做 ActiveAuthenticationSchemes 的东西,那么这个东西是干什么用的呢?

ActiveAuthenticationSchemes 就是对应着中间件Options里面配置的 AuthenticationScheme ,如果你不指定的话,在使用多个身份验证组件的时候会有问题,会有什么问题呢?往下看

AutomaticAuthenticate

AutomaticAuthenticate 很简单,是一个bool类型的字段,用来配置是否处理 AuthenticationHandler 是否处理请求。或者你可以理解为中间件是不是自动的处理认证相关的业务。

AutomaticChallenge

这个重要哦! 当我们使用多个身份验证中间件的时候,那么就要用到这个配置项了,该配置项是用来设置哪个中间件会是身份验证流程中的默认中间件,当代码运行到 Controller 或者 Action 上的 [Authorize] 这个标记的时候,就会触发身份验证流程。默认情况下MVC的Filter会自动的触发[Authorize],当然也有一种手动触发Authorize的办法就是使用HttpContext.Authentication.ChallengeAsync()

实际上,在验证中间件的管道流程中,应该只有一个组件被设定为 AutomaticChallenge = true,但其实大多数的中间件这个参数默认都是 true ,这些中间件包括(Identity, Cookie, OAuth, OpenId, IISIntegration, WebListener)等, 这就导致了在整个验证流程中会触发多个中间件对其进行相应,这种冲突大部分不是用户期望的结果。

不幸的是,目前框架对于这种情况并没有一个健壮的机制,如果开发人员对于这种机制不是很清楚的话,可能会造成很大的困扰。

幸运的是,ASP.NET Core 团队已经意识到了这个问题,他们将在 NET Standard 2.0 中对此重新进行设计,比如手动触发的时候应该怎么处理,有多个的时候怎么处理,以及会添加一些语法糖。

目前情况下,当有多个验证中间件的时候,应该怎么处理呢?比如同时使用 Identity 和 JwtBearer。正确的做法是应该禁用掉除 Identity 以外的其他中间件的 AutomaticChallenge,然后指定调用的AuthenticationScheme。也就是说在Controller或者Action显式指定 [Authorize(ActiveAuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] ,或者是可以指定一个策略来简化授权调用 [Authorize("ApiPolicy")]

services.AddAuthorization(options =>
{options.AddPolicy("ApiPolicy", policy =>{policy.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme);policy.RequireAuthenticatedUser();});
});

而默认不带参数的 [Authorize] 可以指定AuthorizationPolicie:

services.AddAuthorization(options =>
{options.DefaultPolicy = new AuthorizationPolicyBuilder("Identity.Application").RequireAuthenticatedUser().Build();
});

注意,手动调用 HttpContext.Authentication.ChallengeAsync() 不受 AuthorizationPolicie 影响。

总结

本篇介绍了 ASP.NET Core 有关 Authentication 的几个中间件,然后还有几个比较重要的知识点,这篇文章内容有点多,对于一些人来说可能需要一点时间消化。

相关文章: 

原文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-authentication.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/327226.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mybatis-plus 错误java.lang.NoClassDefFoundError: org/apache/velocity/context/Context

https://blog.csdn.net/qq_39609151/article/details/82855305 mybatis-plus 错误java.lang.NoClassDefFoundError: org/apache/velocity/context/Context Murphy_fly 2018-09-26 16:41:55 27168 收藏 15 分类专栏&#xff1a; 框架 版权 使用mybatis-plus自动生成文件的时…

向上类型转换VS向下类型转换

子类转换成父类时的规则: 将一个父类的引用指向一个子类的对象&#xff0c;称为向上转型(upcasting)&#xff0c;自动进行类型转换。此时通过父类引用调用的方法是子类覆盖或继承父类的方法&#xff0c;不是父类的方法。 此时通过父类引用变量无法调用子类特有的方法&#xff…

.NET 使用 RabbitMQ 图文简介

前言 最近项目要使用RabbitMQ&#xff0c;园里里面已经有很多优秀的文章&#xff0c;Rabbitmq官网也有.net实例。这里我尝试下图文并茂之形式记录下使用的过程。 安装 RabbitMQ是建立在erlang OTP平台下&#xff0c;因此在windows下需要下载并安装以下两个组件&#xff1a; 1. …

Springboot+MyBatis-plus+postgresSQL 的整合

https://blog.csdn.net/xuxiannian/article/details/99625085 SpringbootMyBatis-pluspostgresSQL 的整合 禛陌 2019-08-15 12:20:10 6752 收藏 7 分类专栏&#xff1a; 技术相关 文章标签&#xff1a; SpringbootMyBatis-pluspostgresSQL 版权 磨叨一下 MyBatis-plus 请…

使用Senparc.Weixin.WxOpen开发高可用的微信小程序

Senparc.Weixin SDK介绍 Senparc.Weixin SDk是目前.net平台上使用率最高的微信SDK&#xff0c;除硬件平台暂未发布以外覆盖了所有微信平台模块&#xff0c;自2013年免费开源起已经持续更新了4年&#xff0c;是GitHub上目前Star和Fork数最多的中国C#开源项目。 目前大多数模块都…

Java中的事务——JDBC事务和JTA事务

转载自 Java中的事务——JDBC事务和JTA事务 我的博客中曾经关于事务有过很多讨论&#xff0c;之前的事务介绍基本都是数据库层面的事务&#xff0c;本文来介绍一下J2EE中和事务相关的内容&#xff0c;在阅读本文之前&#xff0c;希望读者对分布式有一定的了解。 关于事务的基础…

IDEA使用笔记(八)——自动生成 serialVersionUID 的设置

Ihttps://www.cnblogs.com/godtrue/p/7674487.html https://www.cnblogs.com/godtrue/p/7674487.html DEA使用笔记&#xff08;八&#xff09;——自动生成 serialVersionUID 的设置 这个设置比较简单&#xff0c;也有一些博文已经写到了&#xff0c;为什么我还要写哪&#…

从事件和DDD入手来构建微服务

领域驱动设计&#xff08;Domain-Driven Design&#xff0c;DDD&#xff09;是一项很伟大的技术&#xff0c;它拉近了设计与程序实际所服务的领域&#xff0c;但是通常我们会关注结构&#xff0c;从而太早地做出决策&#xff0c;这并非DDD的本意。相反&#xff0c;在领域中&…

JTA 深度历险 - 原理与实现

转载自 JTA 深度历险 - 原理与实现 利用 JTA 处理事务 什么是事务处理 事务是计算机应用中不可或缺的组件模型&#xff0c;它保证了用户操作的原子性 ( Atomicity )、一致性 ( Consistency )、隔离性 ( Isolation ) 和持久性 ( Durabilily )。关于事务最经典的示例莫过于信…

为了支持AOP的编程模式,我为.NET Core写了一个轻量级的Interception框架[开源]

ASP.NET Core具有一个以ServiceCollection和ServiceProvider为核心的依赖注入框架&#xff0c;虽然这只是一个很轻量级的框架&#xff0c;但是在大部分情况下能够满足我们的需要。不过我觉得它最缺乏的是针对AOP的支持&#xff0c;虽然这个依赖注入框架提供了扩展点使我们可以很…

装箱VS拆箱

我们一般将“基本数据类型转换成包装类”的过程叫做装箱&#xff0c;将“包装类转换成基本数据类型”的过程叫做拆箱。 装箱可以分为手动装箱和自动装箱&#xff1a; 拆箱也可以分为手动拆箱和自动拆箱&#xff1a;

事务模型与分布式事务总结思考

转载自 事务模型与分布式事务总结思考 1. 介绍 之前了解过一些分布式事务处理的思想&#xff0c;包括MVCC、TCC等。但是对具体实现的规范和约束还不够理解清晰。本文从事务模型分类来讨论常见的事务模型。事务模型的含义&#xff0c;应该指的是我们如何去使用可控制事务。 首…

我的这10年——从机械绘图 到 炼油 到 微软MVP 的华丽转身

年底了&#xff0c;各种总结计划满天飞&#xff0c;有空的时候我也一直在思考这么多年&#xff0c;是怎么过来的。也曾经很迷茫&#xff0c;希望经验和经历能给大家一点带来一点正能量的东西。10年很长&#xff0c;10年前说实话我没有思考过现在的样子&#xff0c;但10年前的日…

基本类型和字符串互相转换

将基本数据类型转换成字符串 将字符串转换成基本数据类型

C#如何使用ES

Elasticsearch简介 Elasticsearch &#xff08;ES&#xff09;是一个基于 Lucene 的开源搜索引擎&#xff0c;它不但稳定、可靠、快速&#xff0c;而且也具有良好的水平扩展能力&#xff0c;是专门为分布式环境设计的。 Elasticsearch是什么 Elasticsearch是一个基于Apache Luc…

springboot中配置mybatis连接postgresql

https://blog.csdn.net/y_qc_lookup/article/details/80178545 springboot中配置mybatis连接postgresql 置顶 Dylans 2018-05-03 15:49:46 41415 收藏 8 分类专栏&#xff1a; java 文章标签&#xff1a; springboot mybatis postgresql xml 版权 最近在使用springboot用于…

CoreCLR源码探索(二) new是什么

前一篇我们看到了CoreCLR中对Object的定义&#xff0c;这一篇我们将会看CoreCLR中对new的定义和处理new对于.Net程序员们来说同样是耳熟能详的关键词&#xff0c;我们每天都会用到new&#xff0c;然而new究竟是什么&#xff1f; 因为篇幅限制和避免难度跳的太高&#xff0c;这一…