使用cookie来做身份认证
文章是msdn的官方文档,链接在这里。其实也有中文的文档,这里还是想做一个记录。
文章有asp.net core 2.x 和1.x 版本,我这里就忽略1.x了。
下面先说几点额外的东西有助于理解。
Authentication 和 Authorization
这里先讲一下Authentication和Authorization两个词的区别。
Authentication:认证。
Authorization:授权。
简单来说,认证是用来证明一个人的身份,比如说他是一个学生,一个老师,一个boss,那么就需要这么一个认证。授权是用来表示这个用户能做什么事情,比如admin可以修改删除数据,normal user只能查看数据。
Issuer 和 Audience
Issuer:发行者,这里来说就是 cookie 是谁分发的。
Audience:听众,这个 cookie 的受众是谁。
正文
就像你前面看到认证相关的主题,Asp.net core Identity 是一个创建用户和维护用户登录的完备的认证解决方案。但有时你可能也想要自己的基于cookie的认证方式。你可以在不使用Asp.net core Identity的情况下使用cookie来实现一种独立的认证服务。
示例源码在这里。
因为我们这里只是做一个demo程序,所以写死一个假设的用户Maria Rodriguez到系统里面。邮箱相关的用户名是“maria.rodriguez@contoso.com”,密码任意。用户通过Pages/Account/Login.cshtml.cs 文件中的AuthenticateUser方法做认证。现实环境中应该基于数据库。
更多如何从ASP.net Core 1.x 到2.0的信息参考这里.
想使用ASP.net Core Identity,参考这里.
配置
如果程序没有使用Microsoft.AspNetCore.App元程序包,给程序引用一下Microsoft.AspNetCore.Authentication.Cookies(版本≥2.1.0)。
在ConfigureServices中,通过Authentication和AddCookie方法添加一下认证服务。
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
传给AddAuthentication的AuthenticationScheme值设置了程序默认使用的认证方案。
AuthenticationScheme在你有多个 cookie 认证实例或者你系统用某种特定的方案来做认证的时候是非常有用的。设置成为CookieAuthenticationDefaults.AuthenticationScheme就表示用‘Cookies’来作为一个方案。你可以设置任意的 string 类型的值来区分不同的方案。
在 Configure方法中,使用UseAuthentication来调用认证中间件用于设置HttpContext.User属性。应在UseMvcWithDefaultRoute和UseMvc 方法之前调用UseAuthentication方法。
AddCookie 设置选项
大致是这么设置:
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.ClaimsIssuer = "test";
options.ClaimsIssuer = "aa";
//以及其他...
});
具体详细的通过CookieAuthenticationOptions来设置相关的选项。着重看几个关键的设置,比如 AccessDeniedPath, LoginPath, LogoutPath, Domain, Name,ExpireTimeSpan。
| 选项 | 描述 |
|---|---|
| AccessDeniedPath | 当HttpContext.ForbidAsync触发302时的跳转地址,默认/Account/AccessDenied |
| ClaimsIssuer | 用于设置 cookie 的Issuer 属性。 |
| Cookie.Domain | cookie的有效域。默认是请求的服务器名。浏览器只会给符合的服务器发送 cookie。你可能会希望设置这个值来调整他的作用域。举个例子,设置成.contoso.com他的作用域就包括contoso.com,www.contoso.com,staging.www.contoso.com等。 |
| Cookie.Expiration | 获取或设置cookie的有效期。core 2.1+不建议使用。建议是使用ExpireTimeSpan来设置 cookie 的失效时间。 |
| Cookie.HttpOnly | 设置 cookie 是否是只能被服务器访问,默认 true,可以设置成 false 给客户端js 脚本访问,但是有可能会造成XSS(跨站脚本攻击)。 |
| Cookie.Name | cookie 的名字。 |
| Cookie.Path | 用来隔离同一个服务器下面的不同站点。比如站点是运行在/app1下面,设置这个属性为/app1,那么这个 cookie 就只在 app1下有效。 |
| Cookie.SameSite | 表示浏览器是否允许 cookie 被附加到相同的站点。有几种枚举:SameSiteMode.Strict,只允许相同的站点。SameSiteMode.Lax允许以安全的 http方式附加到不同站点或相同站点。为了支持 OAuth 认证,需要设置成SameSiteMode.Lax。 |
| Cookie.SecurePolicy | 设置是否只允许 https。 |
| DataProtectionProvider | 用于设置创建TicketDataFormat(在表格最后) |
| Events | 设置一些时间的处理程序。比如OnSignedIn,OnSigningOut等,默认是不做任何操作。 |
| EventsType | Events的类型。 |
| ExpireTimeSpan | 设置存储在 cookie 里面的认证票据的过期时间。服务端会验证加密的 ticket 的有效性。在设置了IsPersistent之后也能在 Set-Cookie 头里面返回。默认的过期时间是14天。 |
| LoginPath | HttpContext.ChallengeAsync方法触发302跳转时候的地址。假设设置成/account/login,比如当前访问/secure 返回401,那么会跳转地址/account/login?returnUrl=/secure,当 login 页面生成一个新的登录身份之后,浏览器会跳转到 secure 页面。默认值是/Account/login |
| LogoutPath | 登出地址。 |
| ReturnUrlParameter | 登录或登出之后页面可以做一个跳转,这个跳转地址作为一个参数传过去,这个就用来设置这个参数的名字。 |
| SessionStore | 用来保存跨站点请求的身份信息。设置了之后只有 session 的标识符会发送到客户端。当身份标识比较多的时候可以用。 |
| SlidingExpiration | 滑动过期。标识一个有新的过期时间的新 cookie是否可以被动态的分发。可以在SignInAsync方法里面使用AuthenticationProperties。使用绝对的 cookie 有效期时间来增加应用的安全性。举个例子:```await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, |
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
});``` |
| TicketDataFormat | |
| Validate | 验证当前 option是否是有效的。 |
Cookie Policy Middleware
Cookie 策略中间件。用于设置应用的 cookie的兼容性。和顺序有关,只会影响程序管道他后面的设置。如下方式使用。
app.UseCookiePolicy(cookiePolicyOptions);
CookiePolicyOptions提供了程序全局特性相关的设置。并且可以在 cookie 添加或者删除的时候挂钩一些处理程序。 有以下一些属性。
| 属性 | 描述 |
|---|---|
| HttpOnly | 设置 cookie 是否是只能通过服务器访问的。默认是HttpOnlyPolicy.None |
| CheckConsentNeeded | 一个返回 bool 的函数,如果返回 true 会在弹出一个页面让用户确认使用 cookie |
| ConsentCookie | (这个文档上也没说。。。) |
| MinimumSameSitePolicy | 同站点策略,默认是SameSiteMode.Lax, Asp.net Core2.0+ 可用。 |
| OnAppendCookie | cookie 被追加的时候调用。 |
| OnDeleteCookie | cookie 被删除的时候调用。 |
| Secure | 标识 cookie 是否必须是https. |
创建一个认证 cookie
创建一个包含用户信息的 cookie需要构造一个ClaimsPrincipal。用户信息会被序列化然后保存在cookie 里面。
用必要的 Claim来构造一个ClaimsIdentity,然后调用 SignInAsync 方法。
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Email),
new Claim("FullName", user.FullName),
new Claim(ClaimTypes.Role, "Administrator"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
//AllowRefresh = <bool>,
// Refreshing the authentication session should be allowed.
//ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
//cookie 的绝对过期时间,会覆盖ExpireTimeSpan的设置。
//IsPersistent = true,
//表示 cookie 是否是持久化的以便它在不同的 request 之间传送。设置了ExpireTimeSpan或ExpiresUtc是必须的。
//IssuedUtc = <DateTimeOffset>,
// 凭证认证的时间。
//RedirectUri = <string>
//http 跳转的时候的路径。
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
SignInAsync方法创建一个加密过的 cookie 然后把他添加到当前的 response 中。没有设置AuthenticationScheme的话会使用默认的 scheme。
加密是基于asp.net core 的Data Protection系统实现的,所以,如果程序是部署在多台机器或者做了负载均衡上的话,需要配置 data protection(和当年 asp.net 里面的类似。)
登出
SignOutAsync用来登出当前用户并且删除 cookie。代码如下。
await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
登录和登出需要使用相同的方案名称。也就是一样的AuthenticationScheme。
对后台的改变作出反应
当 cookie 被创建之后,它就成了身份标识的唯一来源。即使在后台禁用了当前用户,因为 已经分发的cookie 无法知晓,所以用户依旧可以保持登录状态直到 cookie 失效。
ValidatePrincipal事件可以用来拦截或者覆盖 cookie 的身份验证。这可以减少被收回权限的用户对系统损害的风险。可以通过如下方式实现这个功能。
首先修改一下 SignInAsync 方法里面获取到的用户相关的 claim。
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Email),
new Claim("LastChanged", {数据库的值})//增加一个LastChanged,然后记录一下值
};
var claimsIdentity = new ClaimsIdentity(
claims,
CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
然后创建一个CustomCookieAuthenticationEvents继承自CookieAuthenticationEvents
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
public class CustomCookieAuthenticationEvents : CookieAuthenticationEvents
{
private readonly IUserRepository _userRepository;
public CustomCookieAuthenticationEvents(IUserRepository userRepository)
{
// 从DI 里面获取用户相关的.
_userRepository = userRepository;
}
public override async Task ValidatePrincipal(CookieValidatePrincipalContext context)
{
var userPrincipal = context.Principal;
// 查找上面的LastChanged相关的claim.
var lastChanged = (from c in userPrincipal.Claims
where c.Type == "LastChanged"
select c.Value).FirstOrDefault();
if (string.IsNullOrEmpty(lastChanged) ||
!_userRepository.ValidateLastChanged(lastChanged))//调用的ValidateLastChanged来判断这个lastChanged 相关的额 cookie是否是一个有效的cookie
{
context.RejectPrincipal();//拒绝这个 cookie
await context.HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);// 自动登出
}
}
//其他的方法,都可以设置
public override Task SignedIn(CookieSignedInContext context)
{
return base.SignedIn(context);
}
}
然后通过EventsType来调用这个设置,然后注入这个CustomCookieAuthenticationEvents
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.EventsType = typeof(CustomCookieAuthenticationEvents);
});
services.AddScoped<CustomCookieAuthenticationEvents>();
考虑一种情况,如果说这个用户更新了之后不影响系统的安全,可以考虑替换context.RejectPrincipal() 为context.ReplacePrincipal,并且设置context.ShouldRenew=true来无损的更新用户的principal。
上面的实现方法会在每个请求的时候都触发,所以会对系统的性能造成一定的影响。
持久化 cookie
你可能想要持久化 cookie 让他可以在浏览器的不同进程之间使用。cookie 的持久化应该用类似在界面上显示“记住我”的复选框,然后让用户点击的方式来实现。其他类似的机制也行。
下面的代码用来实现 cookie 持久化。
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true
});
如果 cookie 在浏览器关闭期间过期了,浏览器会在下次启动的时候自动删除 cookie。
AuthenticationProperties在 Microsoft.AspNetCore.Authentication命名空间里面。
绝对过期时间
可以用ExpiresUtc来设置绝对过期时间,但必须同时设置IsPersistent,否者这个这个参数会被忽略,同时,这个 cookie 只是当前回话有效。
当在 SignInAsync 方法里面设置了ExpiresUtc,它会覆盖CookieAuthenticationOptions设置了的ExpireTimeSpan。
下面的代码设置了一个20min 有效期的持久化 cookie,其他有效期相关的设置都会被忽略。
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(20)
});
最后就是感慨一下,msdn 好强大。
使用cookie来做身份认证的更多相关文章
- 使用cookie来做身份认证 转载https://www.cnblogs.com/sheldon-lou/p/9545726.html
文章是msdn的官方文档,链接在这里.其实也有中文的文档,这里还是想做一个记录. 文章有asp.net core 2.x 和1.x 版本,我这里就忽略1.x了. 下面先说几点额外的东西有助于理解. A ...
- ASP.NET Core 如何用 Cookie 来做身份验证
前言 本示例完全是基于 ASP.NET Core 3.0.本文核心是要理解 Claim, ClaimsIdentity, ClaimsPrincipal,读者如果有疑问,可以参考文章 理解ASP.NE ...
- ASP.NET CORE中使用Cookie身份认证
大家在使用ASP.NET的时候一定都用过FormsAuthentication做登录用户的身份认证,FormsAuthentication的核心就是Cookie,ASP.NET会将用户名存储在Cook ...
- 在ASP.NET Core中实现一个Token base的身份认证
注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and authorization in ASP.NET Core 在 ...
- [转]NET Core中实现一个Token base的身份认证
本文转自:http://www.cnblogs.com/Leo_wl/p/6077203.html 注:本文提到的代码示例下载地址> How to achieve a bearer token ...
- 基于FormsAuthentication的用户、角色身份认证
一般情况下,在我们做访问权限管理的时候,会把用户的正确登录后的基本信息保存在Session中,以后用户每次请求页面或接口数据的时候,拿到 Session中存储的用户基本信息,查看比较他有没有登录和能否 ...
- NET Core中实现一个Token base的身份认证
NET Core中实现一个Token base的身份认证 注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and au ...
- 基于FormsAuthentication的用户、角色身份认证(转)
一般情况下,在我们做访问权限管理的时候,会把用户的正确登录后的基本信息保存在Session中,以后用户每次请求页面或接口数据的时候,拿到 Session中存储的用户基本信息,查看比较他有没有登录和能否 ...
- C#进阶系列——WebApi 身份认证解决方案:Basic基础认证
前言:最近,讨论到数据库安全的问题,于是就引出了WebApi服务没有加任何验证的问题.也就是说,任何人只要知道了接口的url,都能够模拟http请求去访问我们的服务接口,从而去增删改查数据库,这后果想 ...
随机推荐
- xshell 快速复制打开之前用过的ssh
有时候需要对一个ip地址的服务器进行多个操作,不在一个会话中,需要新建多个同样的会话 然后左键单击+号 出现一个新的会话 然后再右键单击+号 选择之前保存过的ip地址即可快速创建!
- koa+mysql+vue+socket.io全栈开发之前端篇
React 与 Vue 之间的对比,是前端的一大热门话题. vue 简易上手的脚手架,以及官方提供必备的基础组件,比如 vuex,vue-router,对新手真的比较友好:react 则把这些都交给社 ...
- .net core 并发下的线程安全问题
抱歉,其实内容并不如题!!!真正的题目应该为<.net core 并发下由于注入模式引起的线程安全问题> 背景(写测试demo所出现的异常,供大家学习与拍砖): .net core web ...
- 《前端之路》之 Javascript 模块化管理的来世今生
目录 第二章 - 04: Javascript 模块化管理的来世今生 一.什么是模块化开发 1-1.模块化第一阶段 1-2.封装到对象 1-3. 对象的优化 二.模块化管理的发展历程 2-1.Comm ...
- .NETCore 千星项目模块化开发框架 SimplCommerce 详解
SimplCommerce 是 github 上过千星的.netcore 商城示例项目,本文详解他的模块化框架现实思路,其业务(如产品.订单)不作介绍.因作者文笔水平很差,它又很值得学习和推荐,就算不 ...
- python爬虫踩坑教程
我们的目标是爬取下面这个个网址上的2010~2018年的数据 http://stockdata.stock.hexun.com/zrbg/Plate.aspx?date=2015-12-31 获取我们 ...
- Mybatis-Plus入门示例
1.内容: Mybatis-Plus只是在Mybatis的基础上,实现了功能增强,让开发更加简洁高效. Mybatis-Plus并没有修改Mybatis的任何特性. 2.入门示例: 2.1 需求:使用 ...
- 分享 Xamarin.android 关于使用SQLiteOpenHelper的小白经验
关于使用SQLiteOpenHelper的使用,对于小白的我,百度啦相当多的大神的介绍,均未能让我这新手(零基础)成功学会,参考了http://www.cnblogs.com/yaozhenfa/p/ ...
- idea配github
下面步骤的三个前提条件:安装git 安装idea 注册github账号 步骤一.绑定我的github账号与我的计算机 绑定我的github账号与我的计算机之后,便能很方便地上传或者更新我的代码,这需要 ...
- MySQL和SQLyog的配置-安装及遇到的问题
1. 安装MySQL 我先是参考了菜鸟教程上的#Windows 上安装 MySQL #版块的安装教程,不过经历了种种磨难,我最后是按照#此篇博客#安装成功的. 这两篇教程最大的差别是在于下载的版本不同 ...