接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且不是前后端分离的架构(页面端具有服务端处理能力).移动端就不要考虑了,太麻烦.支持前后端分离前给移动端提供认证Api的一般采用JwtBearer认证,可以和IdentityServer4的password模式结合.很适用,但是id4的password模式各客户端必须绝对信任,因为要暴露用户名密码.适合做企业级下所有产品的认证.不支持除企业外的第三方调用.当然id4提供了其他模式.这是题外话.但是场景得介绍清楚.以免误导大家!

1、Cookie认证流程

引入核心认证组件之后,通过扩展的方式引入Cookie认证,微软采用链式编程,很优雅.Net Core的一大特点.

注入Cookie认证方案,指定Cookie认证参数,并指定Cookie认证处理器,先不介绍参数,看看处理器干了什么.

Cookie的核心认证方法,第一步如下:

一些必须的防重复执行操作,没截图,也不介绍了,安全工作,只贴核心代码.第一步,就是去读取客户端存在的cookie信息.

微软在Cookie认证参数中提供了接口,意味者你可以自定义读取Cookie内容的实现,他会把上下文和Cookie的名称传给你,这样就能定制获取Cookie内容的实现.接着解密Cookie内容

微软注入了Core的核心加密组件,大家自行百度,却采用微软默认的实现.所以客户端的cookie内容一般都以加密内容显示.

接着

拿到seesionId的cliam,关于claim不多说,自行百度.core新的身份模型.必须了解的内容.

cookie认证参数中你可以配置SessionStore,意味者你的session可以进行持久化管理,数据库还是redis还是分布式环境自行选择.应用场景是cookie过长,客户端无法存储,那么就可以通过配置这个SessionStore来实现.即分布式会话.微软也提供了扩展.

接着,cookie过期检测.

接着

上面的代码意味着cookie可以自动刷新.通过以下两个参数

如果读取到的客户端的cookie支持过期刷新,那么重新写入到客户端.

ok,如果没有在客户端读取到cookie内容,意味者cookie被清除,或者用户是第一次登陆,直接返回认证失败,如果成功,执行认证cookie校验认证上下文的方法

Events可以在AuthenticationSchemeOptions参数中配置

但是Cookie认证参数提供了默认实现

意味者你可以在注入Cookie认证服务的时候,自定义验证cookie结果的验证实现.

通过CookieAuthenticationOptions的Events属性进行注入.验证完毕,

判断上下文中的ShouldRenew参数,这个你可以根据业务需要执行刷新cookie的实现,最后返回认证结果.

整个流程到这里结束.

2、应用

构建登陆页面和首页,直接网上找了,代码如下:

  1. using Microsoft.AspNetCore.Authentication;
  2. using Microsoft.AspNetCore.Authentication.Cookies;
  3. using Microsoft.AspNetCore.Builder;
  4. using Microsoft.AspNetCore.Hosting;
  5. using Microsoft.AspNetCore.Http;
  6. using Microsoft.Extensions.Configuration;
  7. using Microsoft.Extensions.DependencyInjection;
  8. using Microsoft.Extensions.Hosting;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Security.Claims;
  13. using System.Text.Encodings.Web;
  14. using System.Threading.Tasks;
  15.  
  16. namespace Core.Authentication.Test
  17. {
  18. public class Startup
  19. {
  20. public Startup(IConfiguration configuration)
  21. {
  22. Configuration = configuration;
  23. }
  24.  
  25. public IConfiguration Configuration { get; }
  26.  
  27. public void ConfigureServices(IServiceCollection services)
  28. {
  29. services.AddControllers();
  30. //注入核心认证组件和cookie认证组件
  31. services.AddAuthentication(options =>
  32. {
  33. options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  34. options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
  35. }).AddCookie();
  36. }
  37.  
  38. public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  39. {
  40. app.UseAuthentication();
  41.  
  42. app.UseAuthorize();
  43.  
  44. app.AddLoginHtml();
  45.  
  46. app.AddUserInfoHtml();
  47. }
  48.  
  49. }
  50.  
  51. public static class CustomMiddleware
  52. {
  53. /// <summary>
  54. /// 登陆页面跳过认证组件
  55. /// </summary>
  56. /// <param name="app"></param>
  57. /// <returns></returns>
  58. public static IApplicationBuilder UseAuthorize(this IApplicationBuilder app)
  59. {
  60. return app.Use(async (context, next) =>
  61. {
  62. if (context.Request.Path == "/Account/Login")
  63. {
  64. await next();
  65. }
  66. else
  67. {
  68. var user = context.User;
  69. if (user?.Identity?.IsAuthenticated ?? false)
  70. {
  71. await next();
  72. }
  73. else
  74. {
  75. await context.ChallengeAsync();
  76. }
  77. }
  78. });
  79. }
  80.  
  81. /// <summary>
  82. /// 注入登陆页面
  83. /// </summary>
  84. /// <param name="app"></param>
  85. /// <returns></returns>
  86. public static IApplicationBuilder AddLoginHtml(this IApplicationBuilder app)
  87. {
  88. return app.Map("/Account/Login", builder => builder.Run(async context =>
  89. {
  90. if (context.Request.Method == "GET")
  91. {
  92. await context.Response.WriteHtmlAsync(async res =>
  93. {
  94. await res.WriteAsync($"<form method=\"post\">");
  95. await res.WriteAsync($"<input type=\"hidden\" name=\"returnUrl\" value=\"{HttpResponseExtensions.HtmlEncode(context.Request.Query["ReturnUrl"])}\"/>");
  96. await res.WriteAsync($"<div class=\"form-group\"><label>用户名:<input type=\"text\" name=\"userName\" class=\"form-control\"></label></div>");
  97. await res.WriteAsync($"<div class=\"form-group\"><label>密码:<input type=\"password\" name=\"password\" class=\"form-control\"></label></div>");
  98. await res.WriteAsync($"<button type=\"submit\" class=\"btn btn-default\">登录</button>");
  99. await res.WriteAsync($"</form>");
  100. });
  101. }
  102. else
  103. {
  104. var userName = context.Request.Form["userName"];
  105. var userPassword = context.Request.Form["password"];
  106. if (!(userName == "admin" && userPassword == "admin"))
  107. {
  108. await context.Response.WriteHtmlAsync(async res =>
  109. {
  110. await res.WriteAsync($"<h1>用户名或密码错误。</h1>");
  111. await res.WriteAsync("<a class=\"btn btn-default\" href=\"/Account/Login\">返回</a>");
  112. });
  113. }
  114. else
  115. {
  116. //写入Cookie
  117. var claimIdentity = new ClaimsIdentity("Cookie");
  118. claimIdentity.AddClaim("));
  119. claimIdentity.AddClaim(new Claim(ClaimTypes.Name, userName));
  120. claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "1@qq.com"));
  121.  
  122. var claimsPrincipal = new ClaimsPrincipal(claimIdentity);
  123.  
  124. await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal);
  125. if (string.IsNullOrEmpty(context.Request.Form["ReturnUrl"])) context.Response.Redirect("/");
  126. else context.Response.Redirect(context.Request.Form["ReturnUrl"]);
  127. }
  128. }
  129. }));
  130. }
  131.  
  132. /// <summary>
  133. /// 注入用户信息页面
  134. /// </summary>
  135. /// <returns></returns>`
  136. public static IApplicationBuilder AddUserInfoHtml(this IApplicationBuilder app)
  137. {
  138. return app.Map("/profile", builder => builder.Run(async context =>
  139. {
  140. await context.Response.WriteHtmlAsync(async res =>
  141. {
  142. await res.WriteAsync($"<h1>你好,当前登录用户: {HttpResponseExtensions.HtmlEncode(context.User.Identity.Name)}</h1>");
  143. await res.WriteAsync("<a class=\"btn btn-default\" href=\"/Account/Logout\">退出</a>");
  144. await res.WriteAsync($"<h2>AuthenticationType:{context.User.Identity.AuthenticationType}</h2>");
  145.  
  146. await res.WriteAsync("<h2>Claims:</h2>");
  147. await res.WriteTableHeader(new string[] { "Claim Type", "Value" },
  148. context.User.Claims.Select(c => new string[] { c.Type, c.Value }));
  149. });
  150. }));
  151. }
  152. }
  153.  
  154. public static class HttpResponseExtensions
  155. {
  156. public static async Task WriteHtmlAsync(this HttpResponse response, Func<HttpResponse, Task> writeContent)
  157. {
  158. var bootstrap = "<link rel=\"stylesheet\" href=\"https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css\" integrity=\"sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u\" crossorigin=\"anonymous\">";
  159. response.ContentType = "text/html";
  160. await response.WriteAsync($"<!DOCTYPE html><html lang=\"zh-CN\"><head><meta charset=\"UTF-8\">{bootstrap}</head><body><div class=\"container\">");
  161. await writeContent(response);
  162. await response.WriteAsync("</div></body></html>");
  163. }
  164. public static async Task WriteTableHeader(this HttpResponse response, IEnumerable<string> columns, IEnumerable<IEnumerable<string>> data)
  165. {
  166. await response.WriteAsync("<table class=\"table table-condensed\">");
  167. await response.WriteAsync("<tr>");
  168. foreach (var column in columns)
  169. {
  170. await response.WriteAsync($"<th>{HtmlEncode(column)}</th>");
  171. }
  172. await response.WriteAsync("</tr>");
  173. foreach (var row in data)
  174. {
  175. await response.WriteAsync("<tr>");
  176. foreach (var column in row)
  177. {
  178. await response.WriteAsync($"<td>{HtmlEncode(column)}</td>");
  179. }
  180. await response.WriteAsync("</tr>");
  181. }
  182. await response.WriteAsync("</table>");
  183. }
  184. public static string HtmlEncode(string content) =>
  185. string.IsNullOrEmpty(content) ? string.Empty : HtmlEncoder.Default.Encode(content);
  186. }
  187. }

ok,开始分析代码,第一步:

中间件放行登陆接口,接着构建页面.页面构建完毕。看登陆方法都干了什么

用户校验通过后,生成ClaimsPrincipal身份证集合,微软关于身份认证的模型都是基于Claim的,所以包括id4、identity登陆组件、等等里面大量使用到了ClaimsPrincipal

接着

向浏览器端写入cookie,刚刚写的完整的流程,清了下cookie,全都没了,醉了.吐槽一下博客园的保存机制,放redis也好的,清下cookie就没了.花了这个多时间.不想在重写一遍了.这个方法,我就大致介绍下核心点.

这个方案最终会调到,完成cookie的写入

第一步

这个过程,可能存在重复登陆的情况.

这里CookieAuthenticationOptions通过Cookie属性,你可以自定义Cookie配置参数,默认实现如下:

微软通过Builder生成器模式实现.不明白,请移步我的设计模式板块,很简单.

接着构建预登陆上下文

这里CookieAuthenticationOptions通过配置Events属性,你可以做一些持久化操作.或者修改参数,兼容你的业务

接着

_sessionKey可能存在已登陆的情况,那就先清除,接着通过配置CookieAuthenticationOptions的SessionStore属性,你可以实现会话持久化,或者分布式会话.自行选择.

接着

向浏览器写入cookie

不多说,一样.你也可以进行持久化操作,或者修改参数

最后

写http头,没啥东西.并进行日志记录操作.

ok,登陆的核心流程到这里介绍,跑下demo

此时没有cookie,输入 admin admin登陆.

ok,登陆成功,cookie写入完毕.清除cookie,跳转到登陆界面.整个流程结束.纯属个人理解,能力有限,有问题,请指正,谢谢.

除远程登陆外,其余登陆流程(Cookie、Jwt)等都大同小异,所以接下去有时间,会分析远程登陆的源码,但是不想浪费太多时间,下一张会分析微软的

授权组件,看看他是如何和认证组件协同工作的.包括如何集成id4、identity、jwtbear完成一整套前端分离架构(且对移动端友好)的认证中心的构建.

.Net Core 认证系统之Cookie认证源码解析的更多相关文章

  1. Django的rest_framework认证组件之局部设置源码解析

    前言: Django的rest_framework组件的功能很强大,今天来我来给大家剖析一下认证组件 下面进入正文分析,我们从视图开始,一步一步来剖析认证组件 1.进入urls文件 url(r'^lo ...

  2. .Net Core 中间件之静态文件(StaticFiles)源码解析

    一.介绍 在介绍静态文件中间件之前,先介绍 ContentRoot和WebRoot概念. ContentRoot:指web的项目的文件夹,包括bin和webroot文件夹. WebRoot:一般指Co ...

  3. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

  4. .Net Core 认证组件之Cookie认证组件解析源码

    接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且 ...

  5. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

  6. NET Core 2.0使用Cookie认证实现SSO单点登录

    NET Core 2.0使用Cookie认证实现SSO单点登录 之前写了一个使用ASP.NET MVC实现SSO登录的Demo,https://github.com/bidianqing/SSO.Sa ...

  7. identityserver4源码解析_3_认证接口

    目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4 ...

  8. Slurm任务调度系统部署和测试(源码)(1)

    1. 概述1.1 节点信息2. 节点准备3. 部署NTP服务器4. 部署LDAP服务器5. 部署Munge认证服务6. 部署Mysql数据库服务7. 部署slurm7.1 创建slurm用户7.2 挂 ...

  9. winserver的consul部署实践与.net core客户端使用(附demo源码)

    winserver的consul部署实践与.net core客户端使用(附demo源码)   前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的 ...

随机推荐

  1. leetcode 1071 Greatest Common Divisor of Strings

    lc1071 Greatest Common Divisor of Strings 找两个字符串的最长公共子串 假设:str1.length > str2.length 因为是公共子串,所以st ...

  2. 深入浅出 Java Concurrency (9): 锁机制 part 4[转]

    本小节介绍锁释放Lock.unlock(). Release/TryRelease unlock操作实际上就调用了AQS的release操作,释放持有的锁. public final boolean ...

  3. vector容量器的应用

    vector容器的应用,感觉最近做的题目还用的挺多 vector与常用数组大部分是相同的,可以进行插入,删除之类的,但是,有些题目,用普通的数组就很容易爆掉,而vector可以动态的根据你所需要的来调 ...

  4. Ceisum官方教程3 -- 空间数据可视化

    原文地址:https://cesiumjs.org/tutorials/Visualizing-Spatial-Data/ 这篇教程教你如何使用Cesium的Entity API去绘制空间数据,如点, ...

  5. Python爬虫笔记【一】模拟用户访问之设置处理cookie(2)

    学习的课本为<python网络数据采集>,大部分代码来此此书. 做完请求头的处理,cookie的值也是区分用户和机器的一个方式.所以也要处理一下cookie,需要用requests模块,废 ...

  6. 作业test

    views Car <template> <div class="car"> <Nav/> <div class="wrap&q ...

  7. spring整合http

    本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接出处:https://blog.csdn.net/qq_3076499,否则保留追究法律责任的权利. 如果 ...

  8. 统一建模语言简介UML

    统一建模语言(Unified Modeling Language,UML)是用来设计软件蓝图的可视化建模语言,1997 年被国际对象管理组织(OMG)采纳为面向对象的建模语言的国际标准.它的特点是简单 ...

  9. 洛谷P5319 奥术神杖

    题意:给你若干个串和一个填了一部分的串.补完这个串使得 (每个串的匹配次数 * 权值) ^ (1 / 所有串匹配次数) 最大. 解:把这个东西随便取一个对数,就变成了分数规划. 二分.然后在AC自动机 ...

  10. Firefox Developer Edition - Mozilla

    冰狐浏览器开发者工具:https://www.mozilla.org/en-US/firefox/developer/ 冰狐浏览器开发者工具:https://www.mozilla.org/en-US ...