.Net Core 认证系统之Cookie认证源码解析
接着上文.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、应用
构建登陆页面和首页,直接网上找了,代码如下:
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; namespace Core.Authentication.Test { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddControllers(); //注入核心认证组件和cookie认证组件 services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }).AddCookie(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseAuthentication(); app.UseAuthorize(); app.AddLoginHtml(); app.AddUserInfoHtml(); } } public static class CustomMiddleware { /// <summary> /// 登陆页面跳过认证组件 /// </summary> /// <param name="app"></param> /// <returns></returns> public static IApplicationBuilder UseAuthorize(this IApplicationBuilder app) { return app.Use(async (context, next) => { if (context.Request.Path == "/Account/Login") { await next(); } else { var user = context.User; if (user?.Identity?.IsAuthenticated ?? false) { await next(); } else { await context.ChallengeAsync(); } } }); } /// <summary> /// 注入登陆页面 /// </summary> /// <param name="app"></param> /// <returns></returns> public static IApplicationBuilder AddLoginHtml(this IApplicationBuilder app) { return app.Map("/Account/Login", builder => builder.Run(async context => { if (context.Request.Method == "GET") { await context.Response.WriteHtmlAsync(async res => { await res.WriteAsync($"<form method=\"post\">"); await res.WriteAsync($"<input type=\"hidden\" name=\"returnUrl\" value=\"{HttpResponseExtensions.HtmlEncode(context.Request.Query["ReturnUrl"])}\"/>"); await res.WriteAsync($"<div class=\"form-group\"><label>用户名:<input type=\"text\" name=\"userName\" class=\"form-control\"></label></div>"); await res.WriteAsync($"<div class=\"form-group\"><label>密码:<input type=\"password\" name=\"password\" class=\"form-control\"></label></div>"); await res.WriteAsync($"<button type=\"submit\" class=\"btn btn-default\">登录</button>"); await res.WriteAsync($"</form>"); }); } else { var userName = context.Request.Form["userName"]; var userPassword = context.Request.Form["password"]; if (!(userName == "admin" && userPassword == "admin")) { await context.Response.WriteHtmlAsync(async res => { await res.WriteAsync($"<h1>用户名或密码错误。</h1>"); await res.WriteAsync("<a class=\"btn btn-default\" href=\"/Account/Login\">返回</a>"); }); } else { //写入Cookie var claimIdentity = new ClaimsIdentity("Cookie"); claimIdentity.AddClaim(")); claimIdentity.AddClaim(new Claim(ClaimTypes.Name, userName)); claimIdentity.AddClaim(new Claim(ClaimTypes.Email, "1@qq.com")); var claimsPrincipal = new ClaimsPrincipal(claimIdentity); await context.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, claimsPrincipal); if (string.IsNullOrEmpty(context.Request.Form["ReturnUrl"])) context.Response.Redirect("/"); else context.Response.Redirect(context.Request.Form["ReturnUrl"]); } } })); } /// <summary> /// 注入用户信息页面 /// </summary> /// <returns></returns>` public static IApplicationBuilder AddUserInfoHtml(this IApplicationBuilder app) { return app.Map("/profile", builder => builder.Run(async context => { await context.Response.WriteHtmlAsync(async res => { await res.WriteAsync($"<h1>你好,当前登录用户: {HttpResponseExtensions.HtmlEncode(context.User.Identity.Name)}</h1>"); await res.WriteAsync("<a class=\"btn btn-default\" href=\"/Account/Logout\">退出</a>"); await res.WriteAsync($"<h2>AuthenticationType:{context.User.Identity.AuthenticationType}</h2>"); await res.WriteAsync("<h2>Claims:</h2>"); await res.WriteTableHeader(new string[] { "Claim Type", "Value" }, context.User.Claims.Select(c => new string[] { c.Type, c.Value })); }); })); } } public static class HttpResponseExtensions { public static async Task WriteHtmlAsync(this HttpResponse response, Func<HttpResponse, Task> writeContent) { 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\">"; response.ContentType = "text/html"; await response.WriteAsync($"<!DOCTYPE html><html lang=\"zh-CN\"><head><meta charset=\"UTF-8\">{bootstrap}</head><body><div class=\"container\">"); await writeContent(response); await response.WriteAsync("</div></body></html>"); } public static async Task WriteTableHeader(this HttpResponse response, IEnumerable<string> columns, IEnumerable<IEnumerable<string>> data) { await response.WriteAsync("<table class=\"table table-condensed\">"); await response.WriteAsync("<tr>"); foreach (var column in columns) { await response.WriteAsync($"<th>{HtmlEncode(column)}</th>"); } await response.WriteAsync("</tr>"); foreach (var row in data) { await response.WriteAsync("<tr>"); foreach (var column in row) { await response.WriteAsync($"<td>{HtmlEncode(column)}</td>"); } await response.WriteAsync("</tr>"); } await response.WriteAsync("</table>"); } public static string HtmlEncode(string content) => string.IsNullOrEmpty(content) ? string.Empty : HtmlEncoder.Default.Encode(content); } }
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认证源码解析的更多相关文章
- Django的rest_framework认证组件之局部设置源码解析
前言: Django的rest_framework组件的功能很强大,今天来我来给大家剖析一下认证组件 下面进入正文分析,我们从视图开始,一步一步来剖析认证组件 1.进入urls文件 url(r'^lo ...
- .Net Core 中间件之静态文件(StaticFiles)源码解析
一.介绍 在介绍静态文件中间件之前,先介绍 ContentRoot和WebRoot概念. ContentRoot:指web的项目的文件夹,包括bin和webroot文件夹. WebRoot:一般指Co ...
- Android源码解析系列
转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...
- .Net Core 认证组件之Cookie认证组件解析源码
接着上文.Net Core 认证系统源码解析,Cookie认证算是常用的认证模式,但是目前主流都是前后端分离,有点鸡肋但是,不考虑移动端的站点或者纯管理后台网站可以使用这种认证方式.注意:基于浏览器且 ...
- .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析
介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...
- NET Core 2.0使用Cookie认证实现SSO单点登录
NET Core 2.0使用Cookie认证实现SSO单点登录 之前写了一个使用ASP.NET MVC实现SSO登录的Demo,https://github.com/bidianqing/SSO.Sa ...
- identityserver4源码解析_3_认证接口
目录 identityserver4源码解析_1_项目结构 identityserver4源码解析_2_元数据接口 identityserver4源码解析_3_认证接口 identityserver4 ...
- Slurm任务调度系统部署和测试(源码)(1)
1. 概述1.1 节点信息2. 节点准备3. 部署NTP服务器4. 部署LDAP服务器5. 部署Munge认证服务6. 部署Mysql数据库服务7. 部署slurm7.1 创建slurm用户7.2 挂 ...
- winserver的consul部署实践与.net core客户端使用(附demo源码)
winserver的consul部署实践与.net core客户端使用(附demo源码) 前言 随着微服务兴起,服务的管理显得极其重要.都知道微服务就是”拆“,把臃肿的单块应用,拆分成多个轻量级的 ...
随机推荐
- Tomcat--远程Debug以及参数配置调优
本文会讲解Tomcat远程Debug调试,Tomcat-manager监控(简单带过),psi-probe监控和Tomcat参数调优.本文基于Tomcat8.5版本. Tomcat远程Debug: 远 ...
- [CQOI2011]放棋子--DP
题目描述: 输入格式 输入第一行为两个整数n, m, c,即行数.列数和棋子的颜色数.第二行包含c个正整数,即每个颜色的棋子数.所有颜色的棋子总数保证不超过nm.N,M<=30 C<=10 ...
- Office2010 破解(Microsoft Toolkit 2.4.3.exe)
这两天破解刚安装好的office2010,总是报错 刚才重新下载了Microsoft Toolkit 2.4.3.exe工具后,破解成功,操作如下: 选择office按钮后,如下操作,
- Spring AOP(三)--XML方式实现
本文介绍通过XML方式实现Spring AOP,在上一篇中已经介绍了通过注解+java配置的方式,这篇文章主要是看XML中怎么配置,直接上代码了: 一.创建一个连接点 1⃣️定义接口 注意⚠️:可以定 ...
- JSP - (Java Server Pages) - Java服务器界面
JSP简介: 在HTML中嵌入Java脚本代码,由应用服务器中的JSP引擎来编译和执行嵌入的Java脚本代码,然后将生成的整个页面信息返回给客户端: 一个JSP页面包含:静态内容(HTML静态文本), ...
- Linux 7.X 解锁用户账号
Linux 7.X 解锁用户账号 使用指令:sudo passwd -u tomcat ,解锁. 但是会有如下提示信息: 因为 55 失败登录而锁定账户 此时,需执行指令:pam_tally2 -u ...
- Oracle时间日期处理方法
https://www.cnblogs.com/plmm/p/7381496.html 1.用于截取年.月.日.时.分.秒 extract()函数 extract(year from sysdate) ...
- php匿名函数与闭包函数
匿名函数:没有名字的函数:并没有牵扯到应用其他函数的变量问题.仅仅是没有名字 $f=function($param){} 闭包:A函数中嵌套着B函数,B程序中有用到A的变量,当外部函数C调用函数A时, ...
- 在银行业中,BP是指什么?
基点 Basis Point(BP)债券和票据利率改变量的度量单位.一个基点等于0.01个百分点,即0.01%,因此,100个基点等于1%.[例]一浮动利率债券的利率可能比LIBOR高10个基点,10 ...
- 2019-8-8-WPF-非客户区的触摸和鼠标点击响应
title author date CreateTime categories WPF 非客户区的触摸和鼠标点击响应 lindexi 2019-08-08 16:48:31 +0800 2019-07 ...