Identity – Without Identity Framework
前言
上一回研究 Authenticate 和 Authorization 已经是 2 年前了.
业务需求一直没有增长, 所以也没有再去提升它了. 但最近业务开始上去了. 荒废的功夫又得拾起来了.
上一回我没有耐心写完 IdentityServer4 和 Angular. 这回希望能写完它. 不打算修改之前的了. 从头开始吧.
这 2 年微软的文档做的越来越好了. 这里就按着文档做一遍 warm up + summary 就好呗.
这个系列会涉及到的技术是:
Razor Pages,
Web API,
Identity,
OpenIDDict Core (thrid party for OIDC + OAuth, 我没有使用 IdentityServer4 了),
Angular
介绍
Authentication (authen) 主要讲的就是登入, 身份验证,
Authorization (autho) 讲的是权限, 登入不表示拥有所有权限. 可以算是 2nd level protection.
用 ASP.NET Core 做 authen 一般上都会用 Identity Framework 插件 (build-in 的), 然后在插件上做一些魔改. 比较少从 0 开始写,
但其实 ASP.NET Core 底层是有一些基础功能的, 它可以让我们从 0 开始写. 这篇主要就是讲这一块.
主要参考
参考: Use cookie authentication without ASP.NET Core Identity
Protect Page
新建一个项目
dotnet new webapp -o WithoutIdentity
在 privacy page 加上 [Authorize] 标签.
Privacy.cshtml.cs
[Authorize]
public class PrivacyModel : PageModel
{
public void OnGet()
{
}
}
这个时候, 访问这个页面的话就会报错了, 因为我们还没有做任何 authen 的配置.

Setup Config
现在去 Program.cs, 添加配置
builder.Services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
Schemes 是方案的意思, 比如 Cookies Schemes, JWT Schemes 等等
CookieAuthenticationDefaults.AuthenticationScheme 是一个 string, 就是方案的名字. AddAuthentication 声明默认使用什么方案.
AddCookie 则是 ASP.NET Core 封装好的一个 Cookie 方案. 这也是最常见的方案, 参数是方案的名字.
另外, 还有一个常见的方案是 JWT (JSON Web Token), 采用前后端分离架构, 通常就会使用这个方案.
JWT 需要安装
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
配置
builder.Services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
services 配置好后就到 middleware.
添加一个 app.UseAuthentication()

注: 位置要对哦.
这时再访问 private page 出现的 error 就不一样了. 它会 redirect 到 Account/Login page.
这个是 default login page 的路径.
参考: CookieAuthenticationDefaults.cs
我们可以通过 options 来修改掉 login path, 还有其它 Cookie 相关的配置.
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => {
options.Cookie.Name = "MyCookieName";
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
options.ReturnUrlParameter = "returnUrl";
options.LoginPath = "/login";
options.LogoutPath = "/logout";
options.AccessDeniedPath = "/access-denied";
});
注: SameSiteMode 默认是 Lax, Lax 级别已经足够我们做 OAuth 了. 所以不需要 set 成 Strict (我只是演示而已)
相关参考:
Work with SameSite cookies in ASP.NET Core
Login Page
接下来, 我们做一个 login page.
当 user 被 redirect 到 login page 时, URL query 会附上一个 returnUrl, 它记入 user 是从哪一个路径被 redirect 过来的. 当登入成功, 我们可以让 user redirect 回去.
在这个 login page 做一个 login button post
public async Task OnPostAsync([FromQuery] string returnUrl)
{
var claimsIdentity = new ClaimsIdentity(new List<Claim> {
new Claim(ClaimTypes.Name, "UserName")
}, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties()
);
}
HttpContext.SignInAsync 是 ASP.NET Core 封装好的方法.
参数 1 是方案的名字,
参数 2 是 Principal, 里面的 claims 是用来做 authorization 的 (最少都要有一个 Name 的 claims, 而已必须是 unique)
参数 3 是 authentication properties 比如 remenber me 之类的配置.
我们不需要做额外写逻辑去 redirect, SignInAsync 会帮我们 redirect to returnUrl

完成后就成功进入 privacy page 了, 上图是 login 后 response cookie, 它的 value 是用 data protection 加密过的哦.
Logout Page
接着做一个 logout page
public async Task OnPostAsync()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}
然后在任何想 logout 的 page 里面写一个 form, post 去 logout page, 并且附上 returnUrl
<form asp-page="/Account/Logout/Index" asp-route-returnUrl="/" method="post">
<button type="submit">Click here to Logout</button>
</form>
这里有一篇对 logout path 的解释: What does the CookieAuthenticationOptions.LogoutPath property do in ASP.NET Core 2.1?
我本来以为 logout path 的意思是你 logout 完成后它会跳转到这个 page
其实不是这样的. 它有点像 login page 是用来做 logout 的. 当调用 SignOutAsync 时, 当前的 path 必须是 options.LogoutPath (我们在 AddCookie 时写进去的) 那么才会触发 redirect
参考一下 Identity Framework 的实现, 这个是 logout button 可以放在任何地方. 它会 post to /Account/Logout page 附上了 returnUrl.

然后 /Account/Logout.cs 是这样

估计 SignInManger.SignOutAsync 里头就调用了 HttpContext.SignOutAsync.
React to back-end changes
这个概念蛮重要的, 之前我也是没有搞明白.
首先要知道, cookie 保存了 user info, 通过 data protection 对称加密来保护.
在验证的时候, 直接解密成功就登入了. 这个流程是不走数据库的.
好处是快, 坏处就是无法即刻注销这个 cookie. 比如说用户换了密码, 他的预想是之前的 cookie 应该就失效了. 但是没有.
那怎么办呢? 微软把这个权衡留给我们决定。
Identity Framework 对此的方案叫 Security Stamp, 它的做法是. 设定一个检测时期, 比如默认是超过 30 分钟就得去数据库检查一次.
检查什么呢? 就是 security stamp 是否和之前一样. 如果一样就表示这段期间用户并没有做出任何会影响到授权的事. 如果有那么就应该要 update 这个 security stamp. 那么期限一到, 检查的时候就会发现.
Identity Framework 检查的时候会跑 5 个 select from table, 性能还是蛮伤的呢.

可以通过 config 去 set 检测的间隔时间
services.Configure<SecurityStampValidatorOptions>(o => {
o.ValidationInterval = TimeSpan.FromSeconds(60);
});
说太多 Identity Framework 了, 讲回 without Identity Framework 的情况下, 它是怎样 work 的
首先是做 principal 的时候放多一个 security stamp claim
然后在 AddCookie 的时候放入一个 Events 拦截

继承 CookieAuthenticationEvents 然后 override 掉 ValidatePrincipal, 里头就可以检查然后 reject 之类的

或者像 identity 这样配置也是可以的

具体 validation 是这样的

判断时间 > 数据库检查 > 不通过就 signout > 通过就 replace principal, replace principal 不是 identity 的功能, 而是 ASP.NET Core 的功能.

Cookie Expires
refer: How to set asp.net Identity cookies expires time
有几个东西跟 expires 息息相关
SlidingExpiration = 这个是说 keep renew cookie, 每次 request 来的时候如果 cookie 是 ok 的 then 就续命.
Persistent = 如果是 false 的话, 那么就是没有 remember me, cookie expires 是 session, 如果是 true 那么 expires 就 base on cookie option 的 ExpireTimeSpan (默认 14 天)
在 SignIn 的时候还可以调绝对过期 ExpiresUtc 它会覆盖掉 option 的 ExpireTimeSpan, 也不会理会 SlidingExpiration 了 (set 绝对的时候要 Persistent true 哦, 因为 false 就是跑 session)

Cookie expires session 就是说当 browser close 它就被清掉. 需要留意的是 chrome, 一但你 set 了 continue where you left off 它就不会清除掉 expires session cookie 了. 即使是你 off pc 也不会...

Identity – Without Identity Framework的更多相关文章
- 第63章 ASP.NET Identity 支持 - Identity Server 4 中文文档(v1.0.0)
提供了基于ASP.NET身份的实现,用于管理IdentityServer用户的身份数据库.此实现是IdentityServer中的扩展点,以便为用户加载身份数据以将声明发送到令牌. 这个支持的仓储位于 ...
- Asp.Net.Identity认证不依赖Entity Framework实现方式
Asp.Net.Identity为何物请自行搜索,也可转向此文章http://www.cnblogs.com/shanyou/p/3918178.html 本来微软已经帮我们将授权.认证以及数据库存储 ...
- VS2013中web项目中自动生成的ASP.NET Identity代码思考
vs2013没有再分webform.mvc.api项目,使用vs2013创建一个web项目模板选MVC,身份验证选个人用户账户.项目会生成ASP.NET Identity的一些代码.这些代码主要在Ac ...
- ASP.NET Identity 简介
翻译自:http://www.asp.net/identity/overview/getting-started/introduction-to-aspnet-identity ,略有改动. 背景:A ...
- ASP.NET Identity系列教程(目录)
$(document).ready(function(){ $("#hide").click(function(){ $(".en").hide(); }); ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇
在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identity,因为它已经对OWIN 有了良好的集成. 在这篇文章中,我主要关注ASP. ...
- 【ASP.NET Identity系列教程(一)】ASP.NET Identity入门
注:本文是[ASP.NET Identity系列教程]的第一篇.本系列教程详细.完整.深入地介绍了微软的ASP.NET Identity技术,描述了如何运用ASP.NET Identity实现应用程序 ...
- 自定义表并实现Identity登录(一)
注意,Microsoft.AspNet.Identity.Core.1.0.0和Microsoft.AspNet.Identity.Core.2.2.1差别太大,需考虑实际项目中用的是哪种,本文是基于 ...
- ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇(转)
ASP.NET MVC 随想录——开始使用ASP.NET Identity,初级篇 阅读目录 ASP.NET Identity 前世今生 建立 ASP.NET Identity 使用ASP.NET ...
- 转载 ASP.NET MVC中使用ASP.NET Identity
转载原地址: http://blog.jobbole.com/90695/ 在之前的文章中,我为大家介绍了OWIN和Katana,有了对它们的基本了解后,才能更好的去学习ASP.NET Identit ...
随机推荐
- 重磅集结!CNCF/VMware/PingCAP/网易数帆/阿里云联合出品云原生生态大会
"云原生(Cloud Native)"这个词在2020年刷屏了.在企业积极进行数字化转型,全面提升效率的今天,云原生被认为是云计算的"下一个时代". 12月16 ...
- Vue3 之 computed 计算属性的使用与源码分析详细注释
目录 计算属性的基本用法 计算属性的源码 shared 工具方法抽离 计算属性的基本用法 computed 一般有两种常见的用法: 一:传入一个对象,内部有 set 和 get 方法,属于Comput ...
- elementplus弹窗可拖拽draggable,点击空白处不消失close-on-click-modal,modal是否去掉遮罩层
<el-dialog :modal="false" v-model="dialogVisible" title="" width=&q ...
- 学习笔记--Java合集
学习笔记--Java合集 JDK8 基础篇 我的第一个Java程序 Java标识符 Java 字面值 Java中的变量 Java中的数据类型 Java 运算符 Java 控制语句 方法 Java方法基 ...
- pytest + allure2.x 踩坑-报告无数据
我按照网上的教程,在用pytest生成完allure可以使用的json数据之后,然后再用allure生成报告,打开,发现我生成的报告中没有数据显示. 1.首先我用pytest生成数据是没有问题的 2. ...
- Golang 高性能 Websocket 库 gws 使用与设计(一)
前言 大家好这里是,白泽,这期分析一下 golang 开源高性能 websocket 库 gws. 视频讲解请关注B站:白泽talk 介绍 gws:https://github.com/lxzan/g ...
- hadoop hive hbase flume sqoop基本操作
top 里的id为cpu空闲度 如果wa为99.8就是负担太重.得停掉一些任务 cat /proc/cpuinfo 查看cpu信息 cat /proc/meminfo 查看内存信息 hadoop基础操 ...
- matplotlib中渐变颜色条转CSS样式(hex格式)——同mapbox中cog的颜色条拉伸显示
matplotlib中渐变颜色条转CSS样式(hex格式)--同mapbox中cog的颜色条拉伸显示 应用场景: 1.适用于mapbox中显示cog影像时,colormap_name拉伸颜色条转换 2 ...
- 【Spring Data JPA】07 Specifications动态查询
[前言说明] 针对CRUD种的查询,因为我们的查询总是具有各种各样的筛选条件 为了我们的程序能够更加适应筛选条件的变化,SpringDataJpa提供了Specifications这种解决方案 Spe ...
- 【转载】 模仿学习:在线模仿学习与离线模仿学习 ———— Imitate with Caution: Offline and Online Imitation
网上闲逛找到的一篇文章,介绍模仿学习的,题目: Imitate with Caution: Offline and Online Imitation 之所以转载这个文章是因为这个文章还是蛮浅显易懂的, ...