概述

上两篇(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. Django进阶一

    目录 表关系创建 django请求生命周期流程 路由层 无名分组 有名分组 反向解析 路由分发 名称空间 虚拟环境 django版本区别 伪静态 视图层 三板斧 JsonResponse前后端交互数据 ...

  2. tomcat服务器基本操作:实现www.baidu.com访问tomcat中项目

    0.实现用其他的域名,而不再使用localhost:8080/xxx,访问tomcat中的项目: (1). 修改访问tomcat的端口号: (2). tomcat配置虚拟主机: (3). 本地DNS解 ...

  3. spring cloud微服务快速教程之(六) 应用监控 spring boot admin

    0-前言 当我们发布了微服务后,我们希望对各个应用的各个运行状况进行一个监控:这个时候spring boot admin,就出场了: spring boot admin:是一个监控和管理spring ...

  4. 开箱即用!使用Rancher 2.3 启用Istio初体验

    本文来自Rancher Labs Rancher的理念是Run Kubernetes Everywhere,Rancher 2.3中许多重大更新,让这一理念的实现又向前一步. 其中,最重要的两个特性是 ...

  5. 2019HDU多校第四场题解

    1001.AND Minimum Spanning Tree 传送门:HDU6614 题意:给你一个又n个点的完全图,点编号从1~n,每条边的权值为被连接的两点编号按位与后的值.现在要你找到最小生成树 ...

  6. Airbnb如何应用AARRR策略成为全球第一民宿平台

    案例背景 基于房东和租客的痛点构建短租平台,但困于缓慢增长 2007年,住在美国旧金山的两位设计师——BrianChesky与Joe Gebbia正在为他们付不起房租而困扰.为了赚点外块,他们计划将阁 ...

  7. go slice与函数传值的理解

    go语言中所有的传值方式都是传值操作. 今天遇到了以下代码: func main(){ slice := make([],) fmt.Println(slice) change(s) fmt.Prin ...

  8. 一个命令解决linux重启nginx就丢失pid文件问题

    sudo nginx -c /etc/nginx/nginx.conf

  9. Activiti邮件任务

    Activiti邮件任务 作者:Jesai 会不会有那么一天,你会妒忌 Activiti有一种任务叫做邮件任务,顾名思义,就是流程办理到邮件任务的时候,系统就会自动的给你发送任务. Activiti所 ...

  10. 02.flask-script

    网址:https://pypi.org/project/Flask-Script/ 文档:https://flask-script.readthedocs.io/en/latest/ 1.安装 2.新 ...