ASP.NET Core Identity 实战(3)认证过程

如果你没接触过旧版Asp.Net Mvc中的 Authorize 或者 Cookie登陆,那么你一定会疑惑 认证这个名词,这太正式了,这到底代表这什么?

获取资源之前得先过两道关卡Authentication & Authorization

要想了解Identity中用户登录之后,后续的访问时怎样识别用户的,那首先我们得了解下认证(Authentication) 和授权(Authorization)的含义

游侠索罗:星球大战外传

主演:阿尔登·埃伦瑞奇 / 艾米莉亚·克拉克 / 唐纳德·格洛沃

猫眼电影演出 广告
购买

Authentication

Authentication就是认证的意思,还举之前公园的例子,我们拿到门票之后,去公园,入口门卫A首先要根据门票确认我们是谁?是老王,还是老赵,门票是不是真的,过期没。这个过程,这个识别来访者是谁的过程就叫做

Authentication(身份认证过程)

那Authorization 又是啥?

这两个单词太像了,甚至他们的释义都很像,以至于我们在不了解他们的时候总是弄混他们,Authorization是授权的意思,上一小节中,门卫A识别出了我们是谁?Ok,我们是老李,那么门卫B能不能让我们进园呢?不一定,门卫B还要再看,门票过期没,上一步已经看过门票是否过期了,为什么还要看呢?事情是这样的:门卫A看到门票过期了,然后在门票副卡上写上“门票过期”四个大字,再写上“认证失败”,但是负责认证的门卫A没有拦着我们,而是继续让我们前进,因为动物园可能因为活动而允许过期门票进入,或者没有门票也可以,那么接下来授权的人门卫B来看门票,他根据动物园切实的情况看,看看许可范围,再问问后台这个人是不是动物园的管理员。经过种种校验,发现,虽然门票过期了,但是今天是开放日,没门票也行,但是我们是普通游客,却来到了管理员通道,所以没让我们进园——授权失败

小结

好了,这就是认证和授权(Authentication & Authorization),两个不同的事,由两个不同的人(或者组件)来做

  • 认证用来确认来者是谁,确认身份(确认之后可能没有身份)

  • 授权用来确认持有此身份的来者能不能访问当前请求的资源

现在,我们要记住认证与授权中的一个要点

认证只确定用户是谁即使认证失败,也不会拦截用户访问,拦截用户访问发生在授权阶段

另外要注意的是 Authentication和Authorization并不属于Identity的一部分,都不属于Identity,它和Identity是相互独立的,然后一起协作。也就是说,即便我们没有使用Identity ,我们有我们自己的用户存储,角色等等和身份权限相关的一切,那么我们可以将我们的成员系统完美的与Asp.Net Core 进行集成,我们可以假设,Identity就是我们写的,然后将其与Asp.Net Core进行集成

那么为什么要将这个与Identity无关的认证过程Authentication放在这里呢?因为它们是协作的 Authentication和Authorization本事就是要与成员系统协作的,在代码上,他们解耦并且独立,但是在事实逻辑上,成员系统和认证授权总是一起使用的,所以一起讲容易理解

那么这篇文章只讲 Authentication与Authorization中的第一个 —— Authentication,先来了解一下,asp.net core 是怎样知道我们已经登陆的访客是谁的


身份认证中间件 Authentication Middleware

中间件(Middleware)讲起来又是一个长长的故事,如果你完全没概念,那么我建议你先简单学习一下asp.net core 中的中间件,你只要知道它的运行原理即可

在一般的asp.net core web 项目中,我们一般把身份认证中间件放在 静态文件中间件之后,Mvc中间件之前

public void Configure(IApplicationBuilder app, IHostingEnvironment env){    app.UseStaticFiles();    app.UseAuthentication();    app.UseMvc(routes =>    {        //略...    }); }


这样做的目的很简单,仅对需要认证的部分做认证

在http请求到达 mvc中间件之前,也就是进入我们写的逻辑代码之前,身份认证就结束了,也就是说,身份认证不能在 controller action中控制

我们用一张图来简化发生在身份认证中间件中的认证过程,注意,这里马上要引入一个新的概念

身份认证 handler

中间件是嵌在中间件管道中的一个一个模块,http请求有条件的流经他们

另一个相似的东西,有很多 handler 嵌在身份认证中间件上,那么http是流经所有的handler吗?

Authentication Handler

Authentication Hander 顾名思义,他就是切实处理身份认证的组件,它附加在 authentication middleware 上,在请求到来时, middleware 会在所有附加在它之上的handler中选取一个用来做身份认证

那么当我们使用Identity时,哪些 authentication handler 被附加了呢?当请求到来时,authentication middleware 如何知道要选择哪个handler呢?

接下来,我们一一解答

Cookie Authentication Handler

Identity只添加了一种类型的 handler ——CookieAuthenticationHandler

在我们的StartUp类中的ConfigureServices方法中,我们添加了Identity的Service

services.AddIdentity<ApplicationUser, IdentityRole>()    .AddEntityFrameworkStores<ApplicationDbContext>()    .AddDefaultTokenProviders();

但事情没这么简单,在添加Identity的同时,Identity还未我们的项目添加了AuthenticationService和CookieAuthenticationHandler


public static IdentityBuilder AddIdentity<TUser, TRole>( {   

 // Services used by identity    services.AddAuthentication(options =>    {        options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;        // 略...    })   

 .AddCookie(IdentityConstants.ApplicationScheme, o =>    {    // 略...    

在services.AddAuthentication的内部添加了AuthenticationService

namespace Microsoft.Extensions.DependencyInjection{  

 public static class AuthenticationCoreServiceCollectionExtensions    {        

public static IServiceCollection AddAuthenticationCore(this IServiceCollection services)  

     {            services.TryAddScoped<IAuthenticationService, AuthenticationService>();            services.TryAddScoped<IAuthenticationHandlerProvider, AuthenticationHandlerProvider>();            services.TryAddSingleton<IAuthenticationSchemeProvider, AuthenticationSchemeProvider>();

注意上面代码的最后三行,后面涉及到他们的获取,他们就是在此处添加的

namespace Microsoft.Extensions.DependencyInjection{  


 public static class CookieExtensions    {        


public static AuthenticationBuilder AddCookie(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<CookieAuthenticationOptions> configureOptions)     

   {            builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureCookieAuthenticationOptions>());        

    return builder.AddScheme<CookieAuthenticationOptions, CookieAuthenticationHandler>(authenticationScheme, displayName, configureOptions);


上方的代码添加了 CookieAuthenticationHandler

在添加Authentication service的同时,制定默认的 authentication scheme(这个概念在之前的文章中提到过,如果你还有印象的话) 是谁(就是下方的cookie authentication handler)

这时候另一个问题浮现了,只添加了一个 cookie authentication handler,为什么还要将他制定成默认值,是否有有点多此一举呢?

虽然Identity只添加了一种类型的 handler(cookie authentication handler),但是他同时添加了多个

在 authentication 中间件上,区分各个handler的方法是指定不同的 authentication scheme,而不是通过 handler 的类型

其实它添加了这么多:

services.AddAuthentication(options =>

{

    options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;

    options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;

    options.DefaultSignInScheme = IdentityConstants.ExternalScheme;

})

.AddCookie(IdentityConstants.ApplicationScheme, 略)

.AddCookie(IdentityConstants.ExternalScheme, 略)

.AddCookie(IdentityConstants.TwoFactorRememberMeScheme, 略)

.AddCookie(IdentityConstants.TwoFactorUserIdScheme,略);

不过我们暂时不用关心这些是什么

目前为止我们已经知道了这样几件事:

  1. 添加Identity时,Identity添加了用于身份认证的服务,以及默认激活的用于认证的handler——CookieAuthenticationHandler

  2. Identity的身份认证基于cookie (上篇文章我们了解到 Identity在登陆时将票据加密写入了cookie)

  3. 所以用户登录后再次访问的时候,会通过cookie来识别当前用户是谁

动手实践

这一小节中我们先编写测试代码,来看看认证过程产生了哪些结果

创建一个名为TestAuthController的控制器,代码大致如下:

namespace IdentityInAction.Controllers

{

    public class TestAuthController : Controller

    {

        public IActionResult Index()

        {

            return Json(new

            {

                User.Identity.IsAuthenticated,

                User.Identity.AuthenticationType,

                Claims=User.Claims.Select(c => new { c.Type, c.Value })

                // 略...

这些代码将返回一个json字符串,内容是 authentication的部分结果和用户的claims信息,这三行核心代码的意思分别是:

  • 用户是否已经通过身份认证

  • 对次请求进行认证的handler的名称(在上篇文章中我们有提到 authentication scheme 就是 authentication type的另一个名字,记住这件事对我们的理解很有帮助)

  • 这个用户的Claims信息

运行程序,不要进行登陆,如果已经登陆了则退出登陆,退出登陆的链接是右上角的LogOut

然后访问http://localhost:{你的端口}/testauth/index,得到的结果如下:

{  "isAuthenticated": false,  "authenticationType": null,  "claims": [] }

由于没有用户登陆,所以结果里几乎什么都没有,然后再尝试登陆后再次访问这个地址,结果如下:

{

  "isAuthenticated": true,

  "authenticationType": "Identity.Application",

  "claims": [

    {

      "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",

      "value": "78a032c7-0d67-4cec-b031-2d15a7bac755"

    },

    {

      "type": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",

      "value": "abc@abc.com"

    },

    {

      "type": "AspNet.Identity.SecurityStamp",

      "value": "babbb46b-6ba0-4b87-875a-92088197dfbf"

    }

  ]

}


"isAuthenticated": true 这代表认证成功

"authenticationType": "Identity.Application"这是对该请求进行认证的handler的名字,由前文我们知道,我们默认的handler是名为IdentityConstants.ApplicationScheme的cookie handler,我们看一小段源代码证实一下:

public class IdentityConstants

{

    private static readonly string CookiePrefix = "Identity";

    public static readonly string ApplicationScheme = CookiePrefix + ".Application";

正如所料,接下来就是claims了,再上篇文章中提到再登陆过程中加入到Identity的claims有这些:

  • UserName | http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier

  • UserId| http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name

  • SecurityStamp(如果支持的话)| AspNet.Identity.SecurityStamp

  • 存储在数据库中的额外Claims(如果支持的话)(注:支持,但当前用户没有)

这些claims随着票据一起加密写到了cookie中,现在他们又随着cookie一起传了回来
要注意的是,即便我们退出登陆后没有身份认证失败了,但是我们仍然获得了这个Uri的访问权限,原因在于认证并不阻止用户,授权才会阻止用户,而我们没又做授权方面的限制

看到这里,我们的认证过程的大体已经清楚了,接下来我们要看下整个认证过程的一点细节,整个过程是自上而下的,看标题为工作的那一列

工作注释
获取IAuthenticationHandlerProvider的实例
获取默认的AuthenticationScheme
使用上一步的scheme获取IAuthenticationService实例
上一步的service通过第一步的IAuthenticationHandlerProvider获取handlerhandler 是 cookie authentication  handler
handler 调用 AuthenticateAsync,这个方法最终调用了handler的HandleAuthenticateAsync①这个方法是事实上执行认证的方法
获取 CookieAuthenticationOptions.Cookie.Name指定的存储票据的cookie的原始字符串这个Name的默认值是`.AspNetCore.Identity.Application`
解密cookie字符串获得AuthenticationTicket的实例
检查是否使用了session存储,如果有则验证是否存在对应的session
检查cookie 是否过期
检查是否需要刷新cookie
创建新的AuthenticationTicket
将AuthenticationTicket中的Principal设置到HttpContext.User上,认证结束在动手做一节中,我们使用的User就是在这个时候被赋值的

需要注意

① 谁进行的验证

Identity的实现比较复杂,兜兜转转最终的验证时由 cookie authentication handler 的 HandleAuthenticateAsync完成的,如果你在看Identity源代码的话,那么直接跳转到这里可以节省时间

怎么验证的

事实上,说的简单一点,就是在登陆的时候,把票据加密写到cookie里,验证的时候

获取cookie >解密 >还原成票据 >把票据塞到http context中

即使是认证失败了,也是这4个步骤,最终 负责授权的组件会检查 http context 中的票据,还会结合其它情况来确定是否允许当前的请求继续进行下去,而我们的逻辑代码中也可以查看票据,根据不同的 认证结果 返回不同的数据

相关文章: 

  • ASP.NET Core Identity 实战(1)——Identity 初次体验

  • ASP.NET Core Identity Hands On(2)——注册、登录、Claim

  • 用 Identity Server 4 (JWKS 端点和 RS256 算法) 来保护 Python web api

  • 基于OIDC(OpenID Connect)的SSO

  • 学习Identity Server 4的预备知识

  • 使用Identity Server 4建立Authorization Server (1)

  • 使用Identity Server 4建立Authorization Server (2)

  • 使用Identity Server 4建立Authorization Server (3)

  • 使用Identity Server 4建立Authorization Server (4)

  • 使用Identity Server 4建立Authorization Server (5)

  • IdentityServer4(10)- 添加对外部认证的支持之QQ登录

  • spring cloud+.net core搭建微服务架构:Api授权认证(六)

原文地址: 

https://www.cnblogs.com/rocketRobin/p/9105720.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com

RocketRobin

Reward

长按二维码向我转账

pic_reward_qrcode.2x3534de.png

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

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

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

相关文章

codeforces gym-101745 D-Stamp Stamp Stamp动态规划

题解 一道很不错的动态规划问题&#xff0c;首先这些印章一定是s的子串。 我们可以枚举s的子串然后进行check。 如何check&#xff0c;成了这道题的关键。 由于盖章的顺序不知道&#xff0c;所以我们可以使用动态规划的方法。 我们定义状态&#xff1a; dp[i][j]dp[i][j]…

【图论】【最短路】【SPFA】【USACO题库】2.4.4 Bessie Come Home回家(jzoj 1274)

Bessie Come Home回家 题目大意: 有n条路连接着一些牧场&#xff0c;每个牧场由‘a-z’&#xff08;没羊&#xff09;和‘A-Y’&#xff08;有一头羊&#xff09;&#xff0c;问哪个有羊的牧场离‘Z’最近 INPUT FORMAT 第 1 行: 整数 P(1< P<10000),表示连接牧场(谷…

nssl1470-X【并查集,素数】

正题 题目大意 将nnn个数分为222个非空的集合&#xff0c;要求两个集合内数的乘积的gcdgcdgcd为111。求方案数 解题思路 拥有相同质因子的数一定要在同一组&#xff0c;枚举质因数然后将拥有的数都用并查集合并然后用联通块数量来求即可。 codecodecode #include<cstdio&…

尝鲜.net core2.1 ——编写一个global tool

本文内容参考微软工程师Nate McMaster的博文.NET Core 2.1 Global Tools用过npm开发都知道&#xff0c;npm包都可以以全局的方式安装&#xff0c;例如安装一个http-server服务&#xff0c;可以使用npm i http-server -g来将http-server包安装到全局环境。安装完之后&#xff0c…

codeforces gym-101755 D-Transfer Window 二分图匹配、递归

题目 题目链接 题意 告诉了n名球员的交换关系&#xff0c;你现在拥有k名球员&#xff0c;你想要其他k名球员(有的在自己队里)。 输出一种交换方案。 题解 第一步、求闭包。 我们需要在原来的交换矩阵上跑可达闭包&#xff0c;即G[i][j]G[i][j]的含义是jj是否能通过i&quo…

【最短路】【Dijkstra】【图论】最小花费(jzoj 2125)

最小花费 题目大意&#xff1a; n个人&#xff0c;一些人之间可以相互转账&#xff0c;但又手续费x%,问A转给B100元至少要多少钱 样例输入 3 3 1 2 1 2 3 2 1 3 3 1 3 样例输出 103.07153164 数据范围限制 对于所有数据&#xff0c; 1 < n <2000。 解题思路…

jzoj1471-Y【bitset,dp,状压】

正题 题目大意 nnn个点mmm条边&#xff0c;边上是0/10/10/1&#xff0c;求有多少种不同的长度为ddd的路径序列 解题思路 我们将ddd分为两半&#xff0c;定义fi,j,sf_{i,j,s}fi,j,s​表示从iii出发到jjj&#xff0c;路径序列状态sss是否可行。 然后我们可以用bitsetbitsetbits…

Comet OJ-栈的数据结构题【线段树】

正题 题目链接:https://cometoj.com/contest/79/problem/E?problem_id4207 题目大意 nnn个栈&#xff0c;要求支持操作 l∼rl\sim rl∼r区间内的栈压入一个数l∼rl\sim rl∼r区间内的栈弹出一个数求第xxx个栈内第kkk个数 解题思路 对于每个询问我们其实就是要求在他之前的第…

codeforces gym-101755 I-Guess the Tree 交互题、分治、树的直径

题目 题目链接 题意 n2h−1&#xff0c;且1≤n≤1023n2h−1&#xff0c;且1≤n≤1023你可以最多询问2.5∗logn12∗n2.5∗log2n1∗n次&#xff0c;任意两点的距离&#xff0c;让你还原一颗完全二叉树。 题解 第一步、肯定要求整棵树的根节点。 由于这是一颗完全二叉树&…

【01背包】最大约数和(jzoj 2126)

最大约数和 题目大意&#xff1a; 选若干个正整数&#xff0c;和不小于S&#xff0c;使每个数的因数&#xff08;不包括本身&#xff09;之和最大 样例输入 11 样例输出 9 数据范围限制 对于30%的数据&#xff0c; S<10&#xff1b; 对于100%的数据&#xff0c; S …

微软75亿收购Github,微软以开发者为中心的初心不变

前天关于微软要收购 GitHub 的消息传出后&#xff0c;很多人都纷纷讨论&#xff0c;希望 GitHub 能够独立存在&#xff0c;不被任何大厂收购&#xff0c;可是 GitHub 也要生存啊。那又有人说&#xff1a;希望是 Google 来收购 GitHub &#xff0c;而不是微软&#xff0c;在大家…

nssl1476-联【线段树】

正题 题目大意 无限长的010101序列&#xff0c;每次进行一个操作 区间内赋值为000区间内赋值为111区间取反 求第一个000的位置 解题思路 离散化&#xff08;储存每个区间的左右端点和他们加一之后的值&#xff09;后可以用线段树储存第一个000和第一个111的位置。然后区间取…

洛谷-P3396 哈希冲突 分块

题目 题目链接 题意 给你个数列&#xff0c;编号为1…n1…n。 给出两种操作&#xff1a; 查询操作&#xff1a;查询所有编号模xx得y" role="presentation" style="position: relative;">yy的对应数字之和。修改操作&#xff1a;把编号为xx的数…

简明 ASP.NET Core 手册

编者&#xff1a;在4月份推送过这篇文章 简明 ASP.NET Core 手册 &#xff0c;今天再次推荐这篇文章&#xff0c;是因为原作者更新到了新版本1.1.0&#xff0c;改动很大&#xff0c;几乎所有章节都有很大程度的调整&#xff0c;这些调整都是根据读者的建议而做&#xff0c;而且…

【模拟】【递归】电子表格(jzoj 2127)

电子表格 题目大意&#xff1a; 有一个Excel文档&#xff0c;有4中操作&#xff1a;input输入一个格子的值&#xff0c;output输出一个格子的值&#xff0c;sum某个格子等于一块矩形中的总值&#xff08;矩形中的总值改变&#xff0c;这个格子也要改变&#xff09;&#xff0…

nssl1477-赛【对顶堆,贪心】

正题 题目大意 nnn个物品&#xff0c;有两个人&#xff0c;每个人有一些喜欢的物品。 选mmm个物品&#xff0c;至少选择kkk个第一个人喜欢的和kkk个第二个人喜欢的物品 解题思路 首先我们必定是选最小的 我们从小到大枚举选择多少两个人都喜欢的物品iii&#xff0c;然后每人选…

洛谷-P1903 数颜色 分块 bitset

题目 题目链接 题意 给你一个数列代表不同的颜色&#xff08;可以修改&#xff09;。 询问一段区间内有多少种颜色。 题解 很容易想到的就是线段树来维护bitset。 这里为了练习&#xff0c;使用分块维护bitset。 * 事实上线段树可以看成是无限分块。* 修改的时候直接暴…

《你必须掌握的Entity Framework 6.x与Core 2.0》正式出版感想

前言借书正式出版之际&#xff0c;完整回顾下从写博客到写书整个历程&#xff0c;也算是对自己近三年在技术上的一个总结&#xff0c;整个历程可通过三个万万没想到来概括&#xff0c;请耐心阅读。写博、写书完整历程回顾从2013年12月注册博客园账号&#xff0c;注册博客园账号…

2019.01.27【NOIP普及组】模拟赛C组总结

总结 这次比赛的得分是&#xff1a;1001001000300 这次比赛第一题推了一会&#xff0c;推出了公式&#xff0c;马上A掉了&#xff0c;第二题之前做过&#xff0c;当做复习&#xff0c;写了大概半小时&#xff0c;第三题打了一下表&#xff0c;又写了个01背包&#xff0c;A掉&a…

nssl1478-题【dp】

正题 题目大意 nnn个苹果&#xff0c;依次来mmm个人会在两个苹果(ui,vi)(u_i,v_i)(ui​,vi​)中选择一个吃&#xff08;如果都没了就不吃了&#xff09; 求有多少对苹果可以同时存活 解题思路 设gi,jg_{i,j}gi,j​表示iii不吃时jjj是否必吃&#xff0c;我们从后到前枚举边&…