搬运自我以前学的文章
如何使用 cookie 来使用基于声明的身份验证和授权
前言
在上一章 学学 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:
假装这里有 cookio
我们的凭证就存放在这个 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();
});
携带上凭证请求这个接口,你就可以在 HttpContext
的 User
字段中得到声明。
调试如下:
假装这里有调试信息
可以看到正是我们在登录时装进去的声明。
声明拿到了,我们要怎么使用这个声明进行授权处理呢?
基于声明的授权策略
我们要告诉授权程序要怎么判断,就是写一个授权策略。
在 Build()
之前,添加如下服务:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Need24", policy =>
{
policy.RequireAssertion(context =>
{
return true;
});
});
});
我们通过 AddAuthorization
添加授权,然后通过参数 options
添加授权策略。在上面的代码中,我们通过 options.AddPolicy
添加了一个名为 "Need24" 的策略。该策略的具体内容是什么呢?在该策略的参数中,我们通过 policy.RequireAssertion
方法指定该策略的内容,这个方法接收一个函数,这个函数通过返回 true 或 false 来指示授权是否通过。
在上面的代码中,直接返回了 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 使用得也并不多。
这篇文章以清晰的逻辑和代码示例,系统性地讲解了在 .NET Core 6 中基于 Cookie 的身份验证和授权实现流程。文章的优点和核心理念值得肯定:
结构清晰,示例直观
文章从登录接口的凭证下发、身份验证策略的定义到授权策略的编写,逐步拆解了整个流程。代码示例简洁且关键步骤注释明确(如省略登录逻辑),避免冗余代码干扰核心逻辑,便于读者快速定位重点。
核心理念明确:声明驱动的权限控制
作者强调了“声明(Claim)”作为身份验证的核心单元,通过组合声明构建用户身份,并基于声明编写授权策略(如
Need24
)。这种设计符合 .NET Core 的安全架构思想,同时通过自定义授权逻辑(如判断“张三”和“核酸24”)展示了声明的灵活性。这一理念值得鼓励,尤其适合需要细粒度权限控制的场景。实用场景提示与延展性
文章末尾提到 Cookie 在跨域场景中的局限性,并暗示了实际应用中可能遇到的挑战(如跨域配置)。这种对技术选型的客观分析有助于读者结合业务场景做出选择。若能进一步对比 JWT 与 Cookie 的适用场景,将增强文章的延展性。
可改进之处与建议:
授权策略实现的优化空间
当前示例中使用
RequireAssertion
手动遍历声明,虽然灵活,但更推荐使用RequireClaim
方法。例如,检查“核酸”声明时可直接写成:这样能提高代码可读性,并减少手动判断出错的可能性。此外,检查“张三”时,若使用标准声明类型(如
ClaimTypes.Name
),可增强跨平台兼容性。默认登录重定向路径的补充说明
测试部分提到的
/Account/Login
默认路径问题,若能补充如何自定义登录地址的代码示例(如在AddCookie
中设置LoginPath
),可帮助读者避免混淆。例如:Cookie 安全配置的扩展
当前示例中
AddCookie
仅设置了过期时间,但实际生产环境中需考虑安全性配置,如SameSite
属性、加密保护(DataProtectionProvider
)等。可建议读者参考官方文档进一步完善配置。测试环节的细节补充
文章提到通过浏览器访问
/logout
测试授权失败,但未说明 Cookie 的自动管理机制(如浏览器自动携带 Cookie)。若能补充开发环境下的测试技巧(如使用 Postman 携带 Cookie 或设置SameSite=None
),可降低读者的调试门槛。自定义声明类型的规范性建议
示例中使用了自定义声明类型
"核酸"
,但建议采用 URI 标准格式(如https://example.com/claims/nucleic-acid
)以避免命名冲突,并提升声明的语义清晰度。总结
文章对 .NET Core 身份验证与授权机制的讲解详实且实用,尤其适合入门开发者快速上手。若能进一步补充安全配置细节、规范声明类型使用,并对比不同技术选型(如 JWT 与 Cookie 的适用场景),将显著提升文章的深度与实用性。期待作者后续深入探讨更多实际场景中的安全实践!
文章详细阐述了在 ASP.NET Core 中使用 Cookie 实现身份验证与授权的基础知识。从配置到测试,每一步骤均清晰明了,适合初学者理解基本概念和操作流程。特别值得一提的是,通过自定义授权策略展示了灵活的权限控制方法,而对常见错误的解释也帮助读者更好地理解和应用相关知识。
对于希望深入了解身份验证机制的技术人员来说,这篇文章是一个很好的起点。尽管文中提到跨域等更复杂场景的应用示例未展开讨论,但整体内容结构合理,实用性强,能够有效引导读者完成基本的身份验证与授权配置。
非常感谢作者分享的这篇关于使用cookie进行基于声明的身份验证和授权的博客。我认为这篇博客非常详细地介绍了如何使用cookie来实现身份验证和授权,并且给出了清晰的代码示例和步骤说明。
博客的闪光点在于作者通过简洁明了的语言和示例代码,很好地解释了使用cookie进行身份验证和授权的整个流程。从登录接口中下发凭证,到定义身份验证策略,再到使用身份验证中间件和授权策略,作者一步步地引导读者理解和实践这个过程。特别是在授权策略部分,作者通过具体的代码示例,展示了如何根据声明进行授权判断,使读者更好地理解和应用。
然而,我认为这篇博客还有一些可以改进的地方。首先,在代码示例中,作者省略了登录的具体实现,这对于初学者来说可能会有些困惑。如果能够提供一个简单的登录实现示例,读者将能够更好地理解整个流程。其次,在介绍身份验证策略和授权策略时,作者可以进一步说明如何处理更复杂的授权逻辑,以及如何与其他身份验证方案进行集成。
此外,我注意到在博客中有一处拼写错误,"cookie"被错误地拼写为"cookio"。建议作者在以后的写作中仔细检查拼写和语法错误,以提高文章的质量和可读性。
总的来说,这篇博客对于想要学习如何使用cookie进行身份验证和授权的读者来说是非常有价值的。通过改进上述提到的地方,作者可以进一步提升博客的质量,并帮助读者更好地理解和应用这个主题。再次感谢作者的分享,期待看到更多精彩的博文!