前言

Authorize 授权和 Identity Framework 关系不是很大. 虽然 Framework 有帮忙处理 Role, Claims 这些, 但这些只是整个 Authorize 小部分而已.

所以接下来我会用 Without Identity Framework 的方式来介绍 ASP.NET Core 如何实现 Authorize.

Simple Authorize

最常见的权限保护就是阻止页面访问和执行操作

由于 Razor Page 比较难 protect handler, 所以我用 MVC 来做示范 (虽然我的项目更多是防 Web API)

[Authorize]
public class AboutController : Controller
{
public ActionResult Index()
{
return View("~/Controllers/About.cshtml");
} [AllowAnonymous]
public ActionResult Detail()
{
return View("~/Controllers/AboutDetail.cshtml");
}
}

要 protect 整个 Controller 就加上 [Authorize], 要 protect Action 就在 Action 上加 [Authorize]

如果有例外就加上 [AllowAnonymous], 这样就可以了. 简单明了.

需要注意的是, [Authorize] 在 Controller, [AllowAnonymous] 在 Action 是可以的, 这个叫有例外要 by pass.

但是反过来就不可以, 比如 [AllowAnonymous] 在 Controller, [Authorize] 在 Action, 全部 bypass 但有一个例外要 protect, 这个表达是不 ok 的. (当然通常也不会出现这种情况, 因为默认都是不 protect 的丫, 怎么会要在 Controller 上加 AllowAnonymous 呢)

Role-Based

RBAC 是最常用的一种权限机制. Identity Framework 有提供这个封装.

先看看 protect 的时候如何声明需要的 Role

[Authorize(Roles = "Teacher")]
[Authorize(Roles = "Admin, Trainer")]
public ActionResult Index()
{
return View("~/Controllers/About.cshtml");
}

在原本的 Authorize 标签里, 加上指定的 Role.

"Admin, Trainer" 的意思是, 只要满足其中一个就可以了. 这是 " or " 的概念

上下 2 个 Authorize 标签则表示同时需要满足 2 个条件. 这是 " and " 的概念

所以上面整体的意思是

Roles.Contains("Teacher") && (Roles.Contains("Admin") || Roles.Contains("Trainer"))

那这个 Role 是怎样 set 进去 cookie 的呢? 其实它就是一个 claim 而已.

public async Task OnPost()
{
var claimsIdentity = new ClaimsIdentity(new List<Claim> {
new Claim(ClaimTypes.Name, "UserName"),
new Claim(ClaimTypes.Role, "Teacher"), // 这里
new Claim(ClaimTypes.Role, "Trainer") // 这里
}, CookieAuthenticationDefaults.AuthenticationScheme); await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties()
);
}

Identity Framework 在用户登入的时候, 会依据数据库设定好的 Role, Claims 生成 ClaimsPrincipal. 然后就接上 Authorize 的机制了.

要在 Identity Framework 使用 Role 需要额外添加.

Claims-Based

如果 Role 只是一种 Claim, 那么我们当然可以搞出其它的 Claim 验证.

假设需求是: 某些 Action 只有创始人能执行. 创始人不一定非要搞一个 Role 叫创始人

它可以是拥有 EmployeeNumber 1,2,3,4 或 5 的就代表是创始人.

那么我们就做一个 Policy 政策来表示这个逻辑

builder.Services.AddAuthorization(options =>
{
options.AddPolicy("Founders", policy => policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
});

Founders 是政策的名字, 后续的它的验证机制, 要求访问者持有 Claim, Key = EmployeeNumber, Value = 1, 2, 3, 4 或 5

这整套就叫 Claims-Based, 它的底层则是 Policy Based

所以在 ASP.NET Core 可以这么理解: Roled Based 是一种 Claims-Based 是一种 Policy-Based.

[Authorize(Policy = "Founders")]
public ActionResult Index()
{
return View("~/Controllers/About.cshtml");
}

Policy Based

竟然 Policy Based 是老大, 那我们就好好聊聊它的机制.

What is Policy?

首先, Policy 是一个政策, 非常抽象, 你甚至可以认为它就是一个命名而已.

比如我有一个登入要权限管理, 那我就可以叫它 Login Policy. 我也可以把一些条件加入进去, 比如 Working Hour Login Policy.

它是很贴近业务管理的了.

What is Requirement?

如果 Policy 是抽象的命名, 那么具体是什么? 就是 Requirement 条件了.

权限管理就是在讲 “能不能”, 条件自然就是 "能" 的条件咯.

What is Handler?

条件只是一种声明, 具体还需要一个 “人” 来负责执行判断, 这个人就是 handler.

关系图

1 个 Policy 包含多个 requirements, 必须所有的 requirements 都满足才算通过.

1 个 requirement 可以被 1 个或多个 handler 了处理, 同样 1 个 handler 也可以处理多个 requirement.

在处理的过程中, 只要其中一个 handler 说这个 requirement 通过了, 那这个 requirement 就 pass 了, 不需要所有的 handler 都 pass.

这就是 ASP.NET Core Policy Based 的基本机制.

Why so complicated?

可能开始接触会搞不懂为什么它这样设计. Policy 很好理解.

Requirement 和 Handler 拆开主要的目的是为了复用, 它可以多对多搭配来验证, 这样复用性就很高.

记得 Requirement 的职责是表达条件需求, Handler 是具体执行验证.

Policy Based 实战

Requirements

它就是一个很普通的 class implement IAuthorizationRequirement 就可以了, IAuthorizationRequirement 其实是空的, 所以这里的 implement 只是为了声明而已.

里面有什么 property 就看需求咯, 比如 AgeRequirement 里面就可以有 property MinAge

public class MyRequirementA: IAuthorizationRequirement
{
public string WhateverYouWant { get; set; } = "";
}
public class MyRequirementB : IAuthorizationRequirement
{
}
public class MyRequirementC : IAuthorizationRequirement
{
}

Policy with Requirements

在创建 Policy 的时候, 为其添加 Requirements

builder.Services.AddAuthorization(options =>
{
options.AddPolicy(
"MyPolicy",
policy => {
policy.Requirements.Add(new MyRequirementA());
policy.Requirements.Add(new MyRequirementB());
policy.Requirements.Add(new MyRequirementC());
}
);
});

Handlers

For 1 requirement 继承 AuthorizationHandler<TheRequirement>

public class MyHandlerA : AuthorizationHandler<MyRequirementA>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirementA requirement)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}

通过 contextSucceed(requirement) 表示这个 requirement 通过了. 如果不通过的话就不要处理. 留给其它 handler.

For multiple requirements implement IAuthorizationHandler

public class MyHandlerABC : IAuthorizationHandler
{
public Task HandleAsync(AuthorizationHandlerContext context)
{
var pendingRequirements = context.PendingRequirements.ToList(); foreach (var requirement in pendingRequirements)
{
if (requirement is MyRequirementB)
{
context.Succeed(requirement);
}
if (requirement is MyRequirementC)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}

里面可以获取到还没有 succeed 的 requirements, 然后就可以遍历验证了.

Dependency Injection, 所有 Handlers 都需要提供给依赖注入

builder.Services.AddScoped<IAuthorizationHandler, MyHandlerA>();
builder.Services.AddScoped<IAuthorizationHandler, MyHandlerB>();
builder.Services.AddScoped<IAuthorizationHandler, MyHandlerABC>();

Scope Variable in handler process

在验证过程中, handler 可以使用 requirement 中的信息, User 信息, Resource 信息做一个逻辑判断

比如, User 有没有 Claim.Age.Value 小于 AgeRequirement.MinAge,

context.Resource 是一个 object, 如果使用的地方是 Controller 那么它就是 HttpContext.

还有一种使用 policy 的方式是 manual 调用, 这种时候 Resource 就是由调用者负责的, 通常用于 Resource-BasedView-Based

public async Task<ActionResult> IndexAsync([FromServices] IAuthorizationService authorizationService)
{
var result = await authorizationService.AuthorizeAsync(
user: User,
resource: HttpContext, // 要传什么都可以
policyName: "MyPolicy"
); return View("~/Controllers/About.cshtml");
}

这种情况, Handler 可以通过泛型声明 Resource 类型

public class MyHandlerA : AuthorizationHandler<MyRequirementA, DefaultHttpContext>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirementA requirement, DefaultHttpContext resource)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}

总结

Role-Based, Claims-Based 说到底还是 Policy-Based 来的.

也有人把它叫成 ABAC Attribute-Based, 其实概念都差不多.

任何一种权限验证不外乎就是

在一个环境 Enviroment (时间, 地点, 网络等)

一个主体 Subject (User, Claims)

想对一个客体 Object (Controller, Entity, File)

做一个操作 Action (访问, CRUD)

然后检查看是否符合所有条件. 最后能不能通过.

在 ASP.NET Core, 1 个 Policy 就表示一套上面的规则, Subject 就是 User, 里面很多 Claims

Action 一般上是访问 Controller, CRUD Entity.

Object 和 Enviroment 就是 context.Resource. 可以是 HttpContext, 也可以传入一个 Entity 等等.

然后 Handler 依据 requirement 来做判断, 得出结果能不能.

额外小知识

当 Controller 返回 ForbinResult 的时候, 会自动跳转到 Account/AccessDenied 页面, 但在 Web API 的情况通常不希望跳转.

解决方法是不要返回 ForbinResult, 取而代之的是返回 StatusCode 403

return StatusCode(StatusCodes.Status403Forbidden)

Identity – Authorize的更多相关文章

  1. 第9章 使用客户端凭据保护API - Identity Server 4 中文文档(v1.0.0)

    快速入门介绍了使用IdentityServer保护API的最基本方案. 我们将定义一个API和一个想要访问它的客户端. 客户端将通过提供ClientCredentials在IdentityServer ...

  2. ASP.NET Core的身份认证框架IdentityServer4(7)- 使用客户端证书控制API访问

    前言 今天(2017-9-8,写于9.8,今天才发布)一口气连续把最后几篇IdentityServer4相关理论全部翻译完了,终于可以进入写代码的过程了,比较累.目前官方的文档和Demo以及一些相关组 ...

  3. ASP.NET Core的身份认证框架IdentityServer4--(2)API跟WEB端配置

    API配置 可以使用ASP.NET Core Web API模板.同样,我们建议您控制端口并使用与之前一样的方法来配置Kestrel和启动配置文件.端口配置为http://localhost:5001 ...

  4. 【.NET Core】ASP.NET Core之IdentityServer4(1):快速入门

    [.NET Core]ASP.NET Core之IdentityServer4 本文中的IdentityServer4基于上节的jenkins 进行docker自动化部署. 使用了MariaDB,EF ...

  5. .NET Core IdentityServer4实战 第一章-入门与API添加客户端凭据

    内容:本文带大家使用IdentityServer4进行对API授权保护的基本策略 作者:zara(张子浩) 欢迎分享,但需在文章鲜明处留下原文地址. 本文将要讲述如何使用IdentityServer4 ...

  6. IdentityServer4实战 - 与API单项目整合

    一.前言 我们在实际使用 IdentityServer4 的时候,可能会在使用 IdentityServer4 项目添加一些API,比如 找回密码.用户注册.修改用户资料等,这些API与Identit ...

  7. IdentityServer4(7)- 使用客户端认证控制API访问(客户端授权模式)

    一.前言 本文已更新到 .NET Core 2.2 本文包括后续的Demo都会放在github:https://github.com/stulzq/IdentityServer4.Samples (Q ...

  8. IdentityServer4【QuickStart】之使用ClientCredentials流程保护API

    使用ClientCredentials流程保护API 这个示例展示了使用IdentityServer中保护APIs的最基本的场景. 在这个场景中我们会定义一个API和一个想要访问它的客户端.客户端会在 ...

  9. OAuth2认证和授权:ClientCredentials认证

    1:创建授权服务器项目:AuthorizationServer,添加包:IdentityServer4 2:创建资源服务器项目:ResourcesServer,添加包:IdentityServer4. ...

  10. IdentityServer4 中文文档 -9- (快速入门)使用客户端凭证保护API

    IdentityServer4 中文文档 -9- (快速入门)使用客户端凭证保护API 原文:http://docs.identityserver.io/en/release/quickstarts/ ...

随机推荐

  1. [oeasy]python0117 文字的演化_埃及圣书体_象形文字_楔形文字

    埃及圣书体 回忆上次内容 两河流域 苏美尔文明 所使用的 楔形文字 不是象形文字     ​   添加图片注释,不超过 140 字(可选)   楔形文字的字型 究竟是怎么来的呢?   巴别塔 苏美尔的 ...

  2. [oeasy]python0104_指示灯_显示_LED_辉光管_霓虹灯

    编码进化 回忆上次内容 x86.arm.riscv等基础架构 都是二进制的 包括各种数据.指令   但是我们接触到的东西 都是屏幕显示出来的字符   计算机 显示出来的 一个个具体的字型   ​   ...

  3. CF479C 题解

    洛谷链接&CF 链接 题目简述 一个人想要安排期末考试的时间. 有 \(n\) 场考试,每场考试有两个时间 \(x_i,y_i\),一个是老师规定的时间,另外一个是他与老师商量好的考试时间. ...

  4. [rCore学习笔记 014]批处理系统

    写在前面 本随笔是非常菜的菜鸡写的.如有问题请及时提出. 可以联系:1160712160@qq.com GitHhub:https://github.com/WindDevil (目前啥也没有 本章目 ...

  5. SQL:聚集索引和非聚集索引

    聚集(clustered)索引,也叫聚簇索引 定义:数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引. 注:第一列的地址表示该行数据在磁盘中的物理地址,后面三列 ...

  6. JDK工具包:jshell

    JDK工具包:jshell 简介 使用 jshell 工具可以执行 Java 代码,从而立即获取结果. 您可以输入 Java 定义(变量.方法.类等等) 例如: int x = 8 或 Java 表达 ...

  7. CentOS-7离线安装perl

    1.下载相关安装包 CentOS-7 所有rpm包的仓库地址:https://vault.centos.org/7.9.2009/os/x86_64/Packages/ perl-5.16.3-297 ...

  8. “refer to”和“refer to as”在英语中的用法有所不同

    "refer to"和"refer to as"在英语中的用法有所不同,具体区别如下: Refer to "Refer to"意为" ...

  9. 乌克兰学者的学术图谱case1

    0. 人物:米哈伊洛·兹古罗夫斯基Mykhailo Zakharovych Zghurovskyi,也拼写为Mykhailo Zgurovsky,(乌克兰语:Михайло Захарович Згу ...

  10. Apache DolphinScheduler(2.x和3.x版本) 本地环境搭建教程一览

    在迅速变化的技术领域,本地环境的搭建和调试对于软件开发的效率和效果至关重要.本文将详细介绍如何为Apache DolphinScheduler搭建一个高效的本地开发环境,包括2.x和3.x版本的设置方 ...