前言

在上一章 学学 dotnet core 中的身份验证和授权-1-概念 中,我们大致明白了身份验证和授权两者的关系。那么在本文中,我们将使用 cookie 来做一个简单的身份验证和授权。

本文中我使用的是 .net core6,并用 MiniApi 的方式编写。

下发凭证

在上一章中,我们得知客户端必须先从服务器上得到凭证,才能够这个凭证去进行身份验证和授权。所以我们的第一步就是:从服务器得到凭证。

这种下发凭证的接口一般是登录接口,所以,我们来弄一个登录的接口吧。

在 MiniApi 中写一个 Login 接口:

app.MapPost("/login", ( /* 省略登录参数 */ ) => {
// 省略登录步骤
// 假设登录成功后
return Results.Ok();
});

请注意,在本文中重点是介绍身份验证和授权,所以像上面的登录接口,我会省略掉登录的代码,着重在登录成功后如何下发凭证上

在登录成功后,我们便可以将证明当前用户身份的声明,下发到客户端。所以我们要先有声明,然后再使用 cookie 的方式来下发它。

app.MapPost("/login", async (HttpContext context /* 省略登录参数 */) =>
{
// 省略登录步骤,假设登录成功后 // 定义声明
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "张三"),
new Claim("核酸", "24")
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var claimsPrincipal = new ClaimsPrincipal(claimsIdentity); // 下发
await context.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
claimsPrincipal); return Results.Ok();
});

如上面代码,定义声明,其实就是写一个声明列表,将要用的声明都装进去。在下发的时候,我们是下发一个证件包。就像我们上一章讲到的,多个条目组合成一个证件,多个证件组合成一个证件包。

有了证件包后,要怎么下发凭证呢?通过调用 SignInAsync 方法。这个方法在 HttpContext 中,所以要先注入 HttpContext 才能使用。在我们的例子中,我们给 SignInAsync 方法传递了两个参数,一个是你的身份验证策略名,一个是你要下发的证件包。

但是我们还没有定义任何身份验证策略呢,如果现在直接去调用的话,你会发现程序是会报错的。所以我们要定义一个身份验证策略,在下发凭证的时候,就使用这个策略下发。

定义身份验证策略

net core 中有一些内置的身份验证策略,cookie 就是其中之一。我们使用起来也非常简单:

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie();
// 要在 Build 之前加
var app = builder.Build();

你看就这么简单两句话,重点是 AddAuthentication 方法中的参数是一个策略名,你可以用任何字符串,但是要跟我们在下发时使用的策略名是一样的。在这里是因为有这么个常量可以用。然后后面跟着的 AddCookie() 方法,表示来使用 cookie 来进行身份验证。

如果你要对 cookie 有其他的设置,比如设定过期时间,就可以在 AddCookie 的参数里进行设置,如下:

builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = true;
});

还有很多设置,课后慢慢研究。

现在我们再来运行项目,然后请求这个登录接口 /login,成功的话响应会返回 cookie:

我们的凭证就存放在这个 cookie 当中,你看,是加密的。客户端拿到了凭证了。

拿到了凭证还不够,我们要在请求时携带上凭证,经过身份验证程序,得出凭证里的声明。这时就需要使用到我们的身份验证中间件。

身份验证中间件

我们在适当的地方添加身份验证中间件 UseAuthentication(),要在 Build() 之后,UseAuthorization() 之前。

var app = builder.Build();
// Build() 之后
// 添加身份验证中间件
app.UseAuthentication();
// UseAuthorization() 之前
app.UseAuthorization();

UseAuthentication() 这个中间件将会对我们的请求进行处理,并解析出里面的声明。

加上这个中间件后,我们再添加一个登出接口,注入 HttpContext

app.MapGet("/logout", (HttpContext context) =>
{
var user = context.User;
return Results.Ok();
});

携带上凭证请求这个接口,你就可以在 HttpContextUser 字段中得到声明。

调试如下:

可以看到正是我们在登录时装进去的声明。

声明拿到了,我们要怎么使用这个声明进行授权处理呢?

基于声明的授权策略

我们要告诉授权程序要怎么判断,就是写一个授权策略。

Build() 之前,添加如下服务:

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Need24", policy =>
{
policy.RequireAssertion(context =>
{
return true;
});
});
});

我们通过 AddAuthorization 添加授权,然后通过参数 options 添加授权策略。在上面的代码中,我们通过 options.AddPolicy 添加了一个名为 "Need24" 的策略。该策略的具体内容是什么呢?在该策略的参数中,我们通过 policy.RequireAssertion 方法指定该策略的内容,这个方法接收一个函数,这个函数通过返回 truefalse 来指示授权是否通过。

在上面的代码中,直接返回了 true。但是我们是要自己定策略的,我们要规定只有叫张三的,持有 24 核酸的才能通过。我们知道这两个条件可以在声明中获得,那么把代码改成这样:

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Need24", policy =>
{
policy.RequireAssertion(context =>
{
// 获取声明
var claims = context.User.Claims;
// 判断声明
if (claims.FirstOrDefault(t => t.Type == ClaimTypes.Name)?.Value == "张三"
&&
claims.FirstOrDefault(t => t.Type == "核酸")?.Value == "24")
{
return true;
}
return false;
});
});
});

当然啦我们不一定要使用 RequireAssertion 函数,还有其他的函数来根据情况使用,课后慢慢研究。

到了这里,我们就添加了一个名为 "Need24" 的授权策略,接下来,我们要指示这个策略要用在哪个接口上。

我们要用在登出接口 /logout 上,只需要在它的方法前加上 [Authorize("Need24")] 即可,如下:

app.MapGet("/logout", [Authorize("Need24")] (HttpContext context) =>
{
var user = context.User;
return Results.Ok();
});

如此,这个接口便会使用名为 "Need24" 的授权策略。

最后,添加 UseAuthorization() 中间件,千万要注意,授权中间件要添加在身份验证中间件之后。

app.UseAuthentication();
app.UseAuthorization();

测试

到这里,一整个 cookie 身份验证和授权的流程就搭完了,我们启动项目,清楚客户端的 cookie,在没有携带 cookie 的情况下去请求 /logout 接口。因为没有携带 cookie,也就没有携带凭证,而 /logout 接口是需要授权的,所以,意料之中的这个接口是请求不成功的。

由于是 Get 请求,所以我们可以直接在浏览器中导航到 /logout 来查看情况。

但是请求后,你应该会很奇怪地来到这个页面:

为什么会是 404 呢?再看地址栏,变成了 /Account/Login?ReturnUrl=%2Flogout,这又是为什么?

这是因为 cookie 的授权,如果不通过,会重定向到一个地址,这个地址默认是 /Account/Login。我们是可以在 AddCookie() 中修改这个地址的。因为我们没有 /Account/Login 这个接口,所以是 404。这也说明了我们的授权策略起效了。

接下来,我们先请求登录接口,该接口会返回包含我们声明的 cookie。然后我们再携带上 cookie 请求登出接口,此时应该是返回了 200 的。这说明我们的身份验证和授权生效了。

总结

本文中指示简单介绍了 cookie 的身份验证和授权的用法。使用 cookie,在实际情况中还会遇到很多问题,如:跨域的情况。所以 cookie 使用得也并不多。

在下一篇中,我将介绍常用的 JWT 验证。

参考连接

使用 cookie 的身份验证和授权的更多相关文章

  1. 使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)——第1部分

    原文:使用JWT的ASP.NET CORE令牌身份验证和授权(无Cookie)--第1部分 原文链接:https://www.codeproject.com/Articles/5160941/ASP- ...

  2. 学学dotnet core中的身份验证和授权-1-概念

    前言 身份验证: Authentication 授权: Authorization net core 中的身份验证和授权这两个部分,是相辅相成的.当初我在学在部分的时候,是看的 net core 官网 ...

  3. 从零搭建一个IdentityServer——聊聊Asp.net core中的身份验证与授权

    OpenIDConnect是一个身份验证服务,而Oauth2.0是一个授权框架,在前面几篇文章里通过IdentityServer4实现了基于Oauth2.0的客户端证书(Client_Credenti ...

  4. ASP.NET Web API身份验证和授权

    英语原文地址:http://www.asp.net/web-api/overview/security/authentication-and-authorization-in-aspnet-web-a ...

  5. ASP.NET MVC5学习系列——身份验证、授权

    一.什么是身份验证和授权 人们有时对用户身份验证和用户授权之间的区别感到疑惑.用户身份验证是指通过某种形式的登录机制(包括用户名/密码.OpenID.OAuth等说明身份的项)来核实用户的身份.授权验 ...

  6. 接口测试:添加cookie以及身份验证

    添加cookie以及身份验证的接口文档: cookie:存放在本地的一个键值对 session:存放在服务端的一个键值对 学生金币充值接口文档: 一.使用postman进行测试 由于进行学生金币充值操 ...

  7. ASP.NET WEBAPI 的身份验证和授权

    定义 身份验证(Authentication):确定用户是谁. 授权(Authorization):确定用户能做什么,不能做什么. 身份验证 WebApi 假定身份验证发生在宿主程序称中.对于 web ...

  8. ASP.NET MVC5(五):身份验证、授权

    使用Authorize特性进行身份验证 通常情况下,应用程序都是要求用户登录系统之后才能访问某些特定的部分.在ASP.NET MVC中,可以通过使用Authorize特性来实现,甚至可以对整个应用程序 ...

  9. mongo的身份验证和授权

    问题来源 刚装好的mongo,准备登陆进去测一把的,结果就给我报这个错,鄙人是新手,还不太清楚这个,现学一下~ Mongo的身份验证 在上一篇安装mongo的博客中(https://www.cnblo ...

随机推荐

  1. TypeScript学习文档-基础篇(完结)

    目录 TypeScript学习第一章:TypeScript初识 1.1 TypeScript学习初见 1.2 TypeScript介绍 1.3 JS .TS 和 ES之间的关系 1.4 TS的竞争者有 ...

  2. XShell免费版的安装配置教程以及使用教程(超级详细)

    ​一. XShell的作用 XShell可以在Windows界面下来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的.它支持 RLOGIN.SFTP.SERIAL.TELNET.SSH2 ...

  3. python基本数据类型介绍

    数据类型 首先知道什么是数据类型 针对不同的数据类型采用不同的处理方法 --目录-- 一.数据类型之整型 二.数据类型之浮点型 三.数据类型之字串符 四.数据之列表 一.数据类型之整型 1.其实呢就是 ...

  4. Leetcode216/39/40/77之回溯解决经典组合问题

    Leetcode216-组合总和三 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 .该列表不能包含相同的组合两 ...

  5. 关闭Mac的Microsoft AutoUpdate

    最近使用Office 发现AutoUpdate一直会启动.我也不需要里面的更新.每次还要把它推出. 网上看到有两种方法,一种是暴力删除,另一种是通过权限限制. 暴力可不是我喜欢的方式,所以选择后者. ...

  6. mysql的半同步复制

    1. binlog dump线程何时向从库发送binlog mysql在server层进行了组提交之后,为了提高并行度,将提交阶段分为了 flush sync commit三个阶段,根据sync_bi ...

  7. 2021.11.03 P2886 [USACO07NOV]Cow Relays G(矩阵+floyed)

    2021.11.03 P2886 [USACO07NOV]Cow Relays G(矩阵+floyed) [P2886 USACO07NOV]Cow Relays G - 洛谷 | 计算机科学教育新生 ...

  8. 2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?)

    2021.07.19 P2624 明明的烦恼(prufer序列,为什么杨辉三角我没搞出来?) [P2624 HNOI2008]明明的烦恼 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn ...

  9. Dom基础(一):attribute和properrty的区别

    properrty:修改对象属性不会体现到html结构中,针对DOM节点自带属性(id,className,style) attribute:修改html属性,会改变html结构,大多可以添加自定义属 ...

  10. k8s入门之pod(四)

    pod是k8s项目中的最小编排单位,它是运行中的一组(一个或多个)容器,这些容器共享存储.网络.调度等资源,pod是一个逻辑概念,同一个名称空间下不同pod可以通过ip互相访问. 一.通过命令行方式管 ...