概述

上两篇(asp.net core 3.x 身份验证-1涉及到的概念asp.net core 3.x 身份验证-2启动阶段的配置)介绍了身份验证相关概念以及启动阶段的配置,本篇以cookie身份验证为例来大致说明asp.net core中的身份验证原理。如果我们的应用只考虑浏览器使用,且不考虑前后端分离,cookie是最简单的身份验证方式。虽然这样命名,但我们的用户标识并非一定要存到cookie里,asp.net core允许我们存储到任何地方,如:session、自定义基于内存的存储、redis等等。身份验证与asp.net core Identity结合会更简单,它提供了用户管理功能,以及更身份验证相关的辅助类,如:SignManager,不过暂时不将这东东。

还是以宏观上的理解和使用为主。主要涉及如下流程:

  1. 未登录时请求受保护的资源
  2. 登录
  3. 已登录时访问受保护的资源
  4. 注销

其中步骤1、3差别很小。总体流程大致如下图:

先在Startup中做如下配置:

         public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication().AddCookie();
services.AddControllersWithViews();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles(); app.UseRouting();
app.UseAuthentication();
app.UseAuthorization(); app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}

流程1、3、访问受保护的资源

当我们在Startup.Configre中 app.UseAuthentication(); 将注册身份验证中间AuthenticationMiddleware,它负责在请求阶段尝试从请求中获取用户标识,若获取到则赋值给HttpContext.User属性,否则以匿名用户身份继续执行下一个中间件。

AuthenticationMiddleware

流程大致如下:

  1. 遍历程序中所有实现IAuthenticationRequestHandler身份验证处理器,调用其HandleRequestAsync方法,此方法若返回true则终止本次请求
  2. 通过AuthenticationSchemeProvider获取默认身份验证方案
  3. 调用httpContext.AuthenticateAsync(默认方案名)尝试从请求中获取当前用户
  4. 如果前一步获取到用户,则设置到httpContext.User属性上
  5. 无论是否成功获取用户请求都会进入下一个中间件继续后续执行

步骤1中允许AuthenticationHandler在特定情况下终止请求,如果我们将来自定义AuthenticationHandler时实现IAuthenticationRequestHandler可以达到这种目的
步骤3是核心,httpContext.AuthenticateAsync是扩展方法,内部从IOC容器中获取AuthenticationService并调用其同名方法

疑惑:AuthenticationMiddleware始终获取默认身份验证方案然后尝试从请求中获取用户标识,这个默认是通过Startup.ConfigreService中 services.AddAuthentication(默认方案名) 来配置的。但一个系统中可以注册多个身份验证方案(如:同时支持Cookie和JWTBearerToken方式),所以咋处理呢?我目前能想到的是要么纯网站直接用cookie,前后端分离或移动端app默认方案设置为JWTBearerToken;另一种方式再自定义一个AuthenticationMiddleware;但是感觉都很别扭,所以你有好的想法请指教下,不胜感激!

AuthenticationService.AuthenticateAsync

大致执行如下步骤:

  1. 通过AuthenticationHandlerProvider(注入进来的)获取指定(这里就是默认的)身份验证方案名获取关联的身份验证处理器AuthenticationHandler
  2. 调用AuthenticationHandler.AuthenticateAsync()尝试从请求中获取用户
  3. 返回前尝试将用户标识转换成另一个用户标识(且进行缓存,由于AuthenticationService是Scope注册的,所以没问题)

步骤2针对Cookie身份验证来说最终体现为CookieAuthenticationHandler.HandleAuthenticateAsync()
步骤3在将用户标识设置到httpContext.User之前允许我们添加修改用户标识的某些数据。实现方式是定义类实现IClaimsTransformation并将其(推荐单例)注册到依赖注入容器。

CookieAuthenticationHandler.HandleAuthenticateAsync

大致流程如下:

  1. 通过Options.CookieManager获取cookie
  2. 若Options.SessionStore不为空则从其获取票证,否则直接解密cookie得到票证
  3. 处理票证过期(如Option设置了滑动过期,在过期时响应时重新将票证写入cookie)
  4. 回调Events.ValidatePrincipal允许我们修改或替换用户标识

上述步骤多次用到了Options,它是CookieAuthenticationOptions类型的,专门针对基于cookie的身份验证处理器的选项对象,参考:《asp.net core 3.x 身份验证-2启动阶段的配置》,此选项对象的默认赋值行为在PostConfigureCookieAuthenticationOptions.PostConfigre中定义,我们也可以在启动配置时进行定制,
比如我们想直接将票证存储到自定义的存储中时可以参考下面的配置:

  • 自定义一个类实现ITicketStore(参考
  • 配置cookie身份验证方案时修改选项 services.AddAuthentication().AddCookie(opt=> opt.SessionStore = new MySessionStore());

在CookieAuthenticationHandler的其它多个步骤中都可能使用到此选项对象。使用方式都类似。

在前一步骤AuthenticationService.AuthenticateAsync的步骤3中允许我们的代码替换/修改用户标识,在这里的步骤4也允许做类似的事,区别在于前者是针对所有身份验证方案有效的,如默认身份验证方案为JwtBeaerToken时也有效。而后者只是针对cookie身份验证有效。

AuthorizationMiddleware

由于首次请求用户尚未登录,所以context.User为空。授权中间件最终会执行 context.ChallengeAsync(); 最终会执行CookieAuthenticationHandler.HandleChallengeAsync

CookieAuthenticationHandler.HandleChallengeAsync

默认大致流程如下:

  1. 组织登录页地址:Options.LoginPath + QueryString.Create(Options.ReturnUrlParameter, 当前地址);
  2. 回调Events.RedirectToLogin,若是ajax请求则返回401状态码,否则跳转到登录页

默认登录页地址"/Account/Login",参考PostConfigureCookieAuthenticationOptions.PostConfigre默认处理流程如下:

 public Func<RedirectContext<CookieAuthenticationOptions>, Task> OnRedirectToLogin { get; set; } = context =>
{
if (IsAjaxRequest(context.Request))
{
context.Response.Headers[HeaderNames.Location] = context.RedirectUri;
context.Response.StatusCode = ;
}
else
{
context.Response.Redirect(context.RedirectUri);
}
return Task.CompletedTask;
};

我们也可以通过在应用启动时通过选项对象来修改。

 services.AddAuthentication().AddCookie(opt =>
{
opt.LoginPath = "account/mylogin";
opt.Events.OnRedirectToLogin = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
});

流程2、登录

前端提交用户名密码,Action去数据库对比,若成功则以用户id(或一些非常常用的和与授权相关的属性)作为用户标识。然后调用HttpContext.SignInAsync(ClaimsPrincipal),所以最终会执行CookieAuthenticationHandler.HandleSignInAsync

CookieAuthenticationHandler.HandleSignInAsync

大致步骤如下:

  1. 准备一个CookieSigningInContext(=当前上下文+当前身份验证方案+当前身份验证方案关联的选项对象+需要登录用户的标识+cookie选项+额外数据)
  2. 处理过期时间等属性
  3. 回调Events.SigningIn,允许我们在写入cookie之前做调整
  4. 准备票证(=用户 + 身份验证相关属性 + 身份验证方案名 )
  5. 若此身份验证处理器对应的选项配置了Options.SessionStore则使用此存储,否则加密票证写入cookie
  6. 回调Events.SignedIn
  7. 最后回调Events.RedirectToReturnUrl尝试调整会最初的访问页面

流程4、注销

在Action中调用Context.SingOutAsync可以实现注销,在为传入方案名的情况下使用默认身份验证方案进行注销,假设默认是cookie身份验证,则CookieAuthenticationHandler.HandleSignOutAsync将被执行。大致流程如下:
若选项对象配置了Options.SessionStore则从其清空用户标识
回调Events.SigningOut
清除cookie(可能是含sessionKey或包含用户标识的cookie)
通过回调Events.RedirectToReturnUrl跳转到Options.ReturnUrlParameter页面

总结

本篇只是从大方向说了下基于cookie的身份验证原理,对于我们开发者来说只要记住如下几个点就行了,将来有特殊需求时再去看源码。

  • 应用启动时Startup中通过AddAuthencation(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(opt=>{ 配置各种选项/不配置直接使用默认值  });Configure方法中app.UseRouting();后app.UseAuthentication();插入身份验证中间件
  • opt是CookieAuthenticationOptions类型,我们可以通过它来参与到身份验证的过程中去,记得它有个回调函数集合Events属性。这个选项对象的默认值定义在PostConfigureCookieAuthenticationOptions.PostConfigreCookieAuthenticationDefaults,Events属性的类型和默认值参考CookieAuthenticationEvents
  • 通常我们调用身份验证方法是通过HttpContext来调用,它会从IOC容器拿到AuthenticationService调用其同名方法,最终会找到合适的AuthenticationHandler并调用其同名方法

暂时就想到这么多,以后想到再补充吧。

asp.net core 3.x 身份验证-3cookie身份验证原理的更多相关文章

  1. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  2. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  3. ASP.NET Core WebApi基于JWT实现接口授权验证

    一.ASP.Net Core WebApi JWT课程前言 我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再 ...

  4. Asp.Net Core 5 REST API 使用 JWT 身份验证 - Step by Step

    翻译自 Mohamad Lawand 2021年1月22日的文章 <Asp Net Core 5 Rest API Authentication with JWT Step by Step> ...

  5. ASP.NET Core 如何用 Cookie 来做身份验证

    前言 本示例完全是基于 ASP.NET Core 3.0.本文核心是要理解 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NE ...

  6. 用Middleware给ASP.NET Core Web API添加自己的授权验证

    Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做到完全的前后端分离.API的实现方式有很 多,可以用ASP.NET Core.也可以用ASP.NET Web ...

  7. [转]用Middleware给ASP.NET Core Web API添加自己的授权验证

    本文转自:http://www.cnblogs.com/catcher1994/p/6021046.html Web API,是一个能让前后端分离.解放前后端生产力的好东西.不过大部分公司应该都没能做 ...

  8. 在 ASP.NET Core 应用中使用 Cookie 进行身份认证

    Overview 身份认证是网站最基本的功能,最近因为业务部门的一个需求,需要对一个已经存在很久的小工具网站进行改造,因为在逐步的将一些离散的系统迁移至 .NET Core,所以趁这个机会将这个老的 ...

  9. 【ASP.NET Core】使用最熟悉的Session验证方案

    如果大伙伴们以前写过 ASP 或 PHP 之类的,相信各位对基于 Session 的身份验证很熟悉(其实在浏览器端是结合 Cookie 来处理的).这种验证方式是比较早期的,操作起来也不复杂. a.用 ...

随机推荐

  1. 1052 卖个萌 (20 分)C语言

    萌萌哒表情符号通常由"手"."眼"."口"三个主要部分组成.简单起见,我们假设一个表情符号是按下列格式输出的: [左手]([左眼][口][右 ...

  2. Matlab学习过程中的一些小问题

    1.Overload your functions by having variable number of input and output argumernt.Not only can we ov ...

  3. [AI开发]小型数据集解决实际工程问题——交通拥堵、交通事故实时告警

    这篇文章其实主要是想介绍在深度学习过程中如何使用小型数据集,这种数据集样本数量一般在1000以下,有时候甚至只有几百.一般提到神经网络,大家都会说数据量越丰富,准确性越高,但是实际工作中,可能收集不了 ...

  4. dp-最长回文串

    博客 : http://blog.csdn.net/hao_zong_yin/article/details/72730732 问题描述: 求一个序列中的最长回文串,这个串可以不连续 , 如  { 1 ...

  5. 一次jvm调优过程

    jvm调优实战 前端时间把公司的一个分布式定时调度的系统弄上了容器云,部署在kubernetes,在容器运行的动不动就出现问题,特别容易jvm溢出,导致程序不可用,终端无法进入,日志一直在刷错误,ku ...

  6. <c:forEach /c:forEach>中varStatus的作用

    varStatus封装了当前遍历的状态,可以从该对象上查看是遍历到了第几个元素举例如下 <c:forEach items="${cs}" var="c" ...

  7. 为BlueLake主题增加自定义icon图标

    一.前言 hexo 的 Bluelake 主题是我一直在用的,简单大方,很喜欢.但最近有了添加自定义 icon 图标的需求,比如,添加 "地址"."扫一扫".& ...

  8. 团队项目—Beta版本冲刺(2/3)

    团队信息 何全江(队长) 201731024218 胡志伟 201731024240 李元港 201731024232 孟诚成 201731024242 罗俊杰 201731024226 雷安勇 20 ...

  9. C++封装的基于WinSock2的TCP服务端、客户端

    无聊研究Winsock套接字编程,用原生的C语言接口写出来的代码看着难受,于是自己简单用C++封装一下,把思路过程理清,方便自己后续翻看和新手学习. 只写好了TCP通信服务端,有空把客户端流程也封装一 ...

  10. Map梳理

    Map梳理 类型介绍 通用Map:用于在应用程序中管理映射,通常在 java.util 程序包中实现 HashMap.Hashtable.Properties.LinkedHashMap.Identi ...