# 权限系统 RGCA 架构设计

## 目录 <a href="#mu-lu" id="mu-lu"></a>

* 项目核心内容
* 实战目标
* RGCA 四步架构法

### 项目核心内容 <a href="#xiang-mu-he-xin-nei-rong" id="xiang-mu-he-xin-nei-rong"></a>

* 无代码埋点实现对所有 API Action 访问控制管理
* 对 EF Core 实体新增、删除、字段级读写控制管理
* 与 Identity 进行融合集成

### 实战目标 <a href="#shi-zhan-mu-biao" id="shi-zhan-mu-biao"></a>

* RGCA 四步架构法的应用
* 加深对 OPM 和 OPD 的运用
* 为毕业设计打下基础

### RGCA 四步架构法 <a href="#rgca-si-bu-jia-gou-fa" id="rgca-si-bu-jia-gou-fa"></a>

* Requirement：从利益相关者获取需求
* Goal：将需求转化为目标（功能意图）
* Concept：将目标扩展为完整概念
* Architecture：将概念扩展为架构

#### 从利益相关者获取需求 <a href="#cong-li-yi-xiang-guan-zhe-huo-qu-xu-qiu" id="cong-li-yi-xiang-guan-zhe-huo-qu-xu-qiu"></a>

* 受益原则
* 痛点
* 利益相关者
* 需求分类、排序、特征

**受益原则**

好的架构必须使人受益，要想把架构做好，就要专注于功能的涌现，使得系统把它的主要功能通过跨越系统边界的接口对外展示出来

**痛点**

对于企业内部的管理系统而言基本上都会有一个权限管理系统，产品经理在进行产品设计的时候需要先确定每一个菜单，每一个按钮的 key 是什么，再将 key 与权限进行匹配、绑定

但是产品经理无法确定未来所有需要权限管理的地方，所以需要开发人员提前为所有地方设置 key

由于需要设置的 key 有很多，所以可能命名不规范，也有可能会有缺漏，未来涉及到遗漏的地方就需要开发人员重新编码、发布才能满足需求，这是开发企业内部权限管理功能的痛点

**利益相关者**

* 甲方：开发人员，架构师，投资人
* 客户方：开发人员，产品经理，系统运营人员，老板

| 受益人     | 需求                                                                              |
| ------- | ------------------------------------------------------------------------------- |
| 开发者     | 1、集成简单，少写代码；2、功能灵活，可以扩展；3、不要绑死，可以插拔；4、低代码侵入性，不影响业务代码                            |
| 产品经理    | 1、随时可以增加对系统里面功能和数据的权限控制                                                         |
| 系统管理员   | 1、希望能灵活对系统的权限进行配置，适合角色与员工结构，依照通用行业标准进行配置                                        |
| 开发者所属公司 | 1、低成本（划算）；2、不要绑死                                                                |
| 投资人     | 1、通过该项目的完整演示 RGCA 的架构设计过程；2、将此系统开源回馈到社区，以获得更多开发者的支持；3、进一步搜集开发者和企业用户的需求以进行下一步的开发 |

**需求分类、排序、特征**

| 提出人    | 分类   | 需求                                | 优先级 | 分类            | KANO |
| ------ | ---- | --------------------------------- | --- | ------------- | ---- |
| 产品经理   | -    | 随时可以增加对于系统里面功能和数据的权限控制，不需要开发和发布系统 | -   | 总体的意愿         | 基本型  |
| 产品经理   | 权限管理 | 可以对功能和页面进行组合成一个权限给角色，一次配置即可       | 一期  | 必需品           | 基本型  |
| 产品经理   | 数据权限 | 数据可以控制到新增、删除、字段级别的修改              | 一期  | 必需品           | 基本型  |
| 产品经理   | 数据权限 | 可以根据不同的角色、部门、进行查询数据字段的控制          | 推迟  | 必需品           | 反向型  |
| 产品经理   | 数据权限 | 可以控制不同的部门、角色查看的数据（比如只能看我所在部门的数据）  | 推迟  | 必需品           | 基本型  |
| 系统管理员  | 权限管理 | 配置方便（使用一定的行业标准进行设计）               | -   | 必需品           | 无差异型 |
| 系统管理员  | 功能权限 | 可以对系统内的所有页面访问进行权限控制               | 推迟  | 必需品           | 基本型  |
| 系统管理员  | 功能权限 | 可以对系统内的所有页面的按钮进行权限控制              | 推迟  | -             | -    |
| 系统管理员  | 功能权限 | 对后台所有API请求进行权限控制                  | 一期  | -             | -    |
| 开发人员   | 集成   | 集成简单，少写代码                         | -   | 对缺失物品所表现出来的欲望 | 期望型  |
| 开发人员   | 集成   | 功能灵活，可扩展                          | -   | 对缺失物品所表现出来的欲望 | 期望型  |
| 开发人员   | 集成   | 可插拔                               | -   | 必需品           | -    |
| 开发人员   | 集成   | 低代码侵入性，不影响业务代码                    | -   | 必需品           | -    |
| 客户公司高层 | -    | 低成本                               | -   | 总体意愿          | -    |
| 客户公司高层 | -    | 可插拔                               | -   | 对缺失物品所表现出来的欲望 | -    |
| 投资公司   | -    | 低成本投入、快速可验证（敏捷，精益），少走弯路           | -   | 总体的意愿         | -    |
| 投资公司   | -    | 通过该项目的完整演示 RGCA 的架构设计过程（时间为两天）    | -   | 必需品           | -    |
| 投资公司   | -    | 将此系统开源回馈到社区，以获得更多开发者的支持           | -   | 对缺失物品所表现出来的欲望 | -    |
| 投资公司   | -    | 能够在企业生产系统中使用                      | -   | 必需品           | -    |

#### 将需求转化为目标（功能意图） <a href="#jiang-xu-qiu-zhuan-hua-wei-mu-biao-gong-neng-yi-tu" id="jiang-xu-qiu-zhuan-hua-wei-mu-biao-gong-neng-yi-tu"></a>

从系统的顶层角度：定义系统的形式和功能

确定功能意图（功能意图是由主要受益者，主要需求而推导出来的）

形式：通用的权限管理系统

to..by..using

为了...通过...使用

功能：无需代码埋点，通过UI配置改变资源（页面、按钮、数据、API）的可访问性，达到权限控制的目的

过程 + 操作 <= 工具

| 受益者          | 系统管理员 & 产品经理     |
| ------------ | ---------------- |
| 需求？          | 对系统中受保护的资源进行权限保护 |
| 与解决方案无关的操作对象 | 受保护的资源           |
| 与利益相关的属性     | 可访问性，完整性         |
| 操作数的其他属性     | 可配置性，可访问性        |
| 与解决方案无关的过程   | 拦截/保护            |
| 无关过程的属性      | 准确性              |

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FAwZGG1gIyHXHZp9XgiiJ%2F311.jpg?alt=media\&token=a24d373b-b8d6-4bcb-904c-432bce3a06d1)

#### 将目标扩展为完整概念 <a href="#jiang-mu-biao-kuo-zhan-wei-wan-zheng-gai-nian" id="jiang-mu-biao-kuo-zhan-wei-wan-zheng-gai-nian"></a>

在目标阶段提出了与解决方案无关的过程：拦截，一个模糊抽象的过程，没有说明由谁来拦截，以什么方式来拦截

与解决方案无关的操作对象：受保护的资源，一个抽象的对象，由需求导出了对象的分类，但是仍然没有特别具体，没有具体的场景

到了概念阶段需要提出具体解决方案过程：从解决方案不相关，到与解决方案相关

解决方案是帮助我们解决问题的，在目标阶段大致定义了需要解决什么问题，功能层面只是说明了产品的优势

具体的解决方案是在概念阶段提出的，它体现出如何把功能进行详细的描述，所以需要推导到到与解决方案相关的场面

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2F0IOWMV9YO1u2Ewd5zZju%2F312.jpg?alt=media\&token=ef99661b-bac4-4167-95c9-825e92df5d55)

受保护的资源以 API 为例进行推导，定义为 API Action，而拦截在 ASP .NET Core 中表现为 AuthorizationFilter

因为它是一个名词，不能代表一个过程，所以加上 ing 代表一个过程 AuthorizationFiltering

API Action 经过 AuthorizationFiltering 之后变成一个与解决方案相关的东西

在 ASP .NET Core Mvc 里面变成一个 Result，它是 AuthorizationContext 的一个属性

Result 有几种类型：Sucess，Forbiden，Challenge

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FYnNdU06Z01qS9PYfbnaB%2F313.jpg?alt=media\&token=a06e6cbd-5047-465c-ae12-751418f68ee8)

受保护的资源除了 API Action 之外，还可以是 Entity，而所有 EF 的操作最终都放在 DBContext

DBContext 有一个 SaveChanges 的操作，以及一个 ChangeTracker 的属性记录了实体的所有状态

**特化**：变得更具体

从受保护的资源到 API Action 就是一个特化的过程，从目标到概念也是一个特化的过程

**泛化**：变得更抽象

从数据、页面、按钮、API 到受保护的资源就是一个泛化的过程

**拦截的意图**：保护资源

AuthorizationFilter 在 ASP .NET Core 中只能通过在 Action 上面打标签 Authorize 的方式进行拦截

这就是所谓的代码埋点，比如在 entity 上面打标签也是代码埋点

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2F0WEkXNxZHUlLWT5JjcOI%2F314.jpg?alt=media\&token=31405878-e412-4fb8-83da-050e1a591d79)

拦截的上一层是保护资源，拦截是保护资源的一种方式，需要提前定义受保护的资源

除了受保护的资源，对于所有资源需要动态保护，可以通过动态拦截的方式

动态拦截需要实现一个 DynamicAuthorizationFiltering，不能覆盖原有的功能

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FyT5nAiSul78vfHjpwyRx%2F315.jpg?alt=media\&token=cf917001-272a-44ab-9463-69c05056a925)

保护资源的上一层是安全，安全除了保护资源，还有很多其他的事情可以做，比如记录日志

审计日志会记录用户的所有访问记录，企业可以设置权限

**通过这种方式可以不停地将需求往上一层寻找，一直达到最顶层**

除了向上之外还可以向下寻找，延伸出整体概念，通过概念片段的组合，构成完整的整体概念

接下来对拦截这一过程进行展开，展开为一组必须得到执行的内部过程，针对每一个内部过程，选用特定的操作数、过程及工具对象对其进行特化，就可以得到相应的概念片段，这也是一个特化的过程

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FWrYNC6C5yDbynTExolZp%2F316.jpg?alt=media\&token=3b125a96-1f97-471f-b66b-38c7e3b5a510)

* 配置：系统管理员希望对后台所有 API 请求进行权限控制，所以首先需要知道有哪些 API，对每一个 action 需要可以配置
* 赋权：把角色赋权给用户
* 认证：用户认证之后有一个身份
* 授权：基于身份可以进行授权

#### 将概念扩展为架构 <a href="#jiang-gai-nian-kuo-zhan-wei-jia-gou" id="jiang-gai-nian-kuo-zhan-wei-jia-gou"></a>

* 价值通路与系统架构
* 层级分解

**价值通路与系统架构**

从资源变成权限，权限绑定给用户，用户进行登录，登录之后再进行授权

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FLgyGoLXZpYu3Y28i0HDb%2F317.jpg?alt=media\&token=4cb0d5d7-9264-4fbb-ba3a-eee3bef2603a)

资源分为 ActionAccess 和 EntityAccess

EntityAccess 有 CanCreate，CanDelete，EntityName，Key 几个属性，以及每个字段是否允许修改 MemberAccess

同理 ActionAccess 有 Url，Name，DisplayName，Verb 几个属性

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FOWOjKHj6bju5Ef55bPKe%2F318.jpg?alt=media\&token=9946b5fa-d450-4d0a-b3d5-7d15d899c06c)

注册资源分为 Entity Explorer 和 API Explorer

Entity Explorer 通过 DbContext 进行扫描获取需要监听的实体进行注册

API Explorer 通过 IActionDescriptorCollectionProvider 注册 Action

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FKiG6mcHadQmQWi6jTYIh%2F319.jpg?alt=media\&token=d8845243-36ee-4d22-b622-223f362f51b9)

授权有一个拦截器 AuthorizeFilter

ASP .NET Core Identity 有一个基于 Claims 的认证授权机制，它是一个 key:value 的数组

Clamis 属于 User 对象，User 对象属于 HttpContext

AuthorizeFilter 接收 Claims 和 ActionDescriptior，在 Claims 里面可以获取到 Action 的信息，所以两者有关联关系

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FO3UyTbroh0R54IDt6stR%2F320.jpg?alt=media\&token=a61fdc43-7376-4deb-a2de-bb5747632804)

对于赋权这一步需要定义权限 Permission 和角色，将权限和角色输入到赋权，产生一个角色权限 RolePermission

角色权限 RolePermission 是一个组合对象，包含角色与权限

权限和资源之间有一个包含关系，一个权限包含多个资源

至此完成了一条通路：给多个 Action 定义 key 之后，将 key 赋值给角色，角色绑定到用户，用户登录的时候可以获取到一个 Action 的列表，通过 AuthorizeFilter 来进行对比

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FDcbUgNKDogeLFHt0zPOK%2F321.jpg?alt=media\&token=03031731-9da8-4c53-b1f0-4712e4a059d9)

授权由 AuthorizationContext 判断是否有权限

响应分为 API 响应 和 Entity 响应，针对不同的响应有不同的处理方式

对于 API 响应需要判断是否允许有权限，未认证返回401，无权限返回403

对于 Entity 响应需要 Claims 和 EntityAccessList，通过 Claim 和 AccessList 进行对比

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FBzlnOyJj3A4c4YjlXf1W%2F322.jpg?alt=media\&token=3576a0d5-e7ae-48f3-9907-e5358cb7ab51)

用户登录之后得到 User 身份，发起请求产生 ActionRequest

ActionRequest 属于 HttpContext，最后会输入到 AuthorizeFilter

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2F8IxEskpBHW6GsrX8w1cI%2F323.jpg?alt=media\&token=0e419966-9b12-4e97-b1b3-56db729ebbd1)

整个过程从上到下就是这样一个价值通路，并且已经包含了形式对象

从资源到权限，角色，再到角色和用户的绑定，再到授权整个体系，形成了系统架构

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FHNsmxLEKWg4BseeAS60P%2F324.jpg?alt=media\&token=7312ae97-37d6-4a11-869b-cb0ccdff273c)

**层级分解**

首先从系统架构中找到实体对象：资源，权限，角色，用户

资源由 ResourceProvider 提供，分为 ActionResourceProvider 和 EntityResourceProvider

用户和角色使用 ASP .NET Core Identity 的 UserManager 和 RoleManager

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FAh3m27KbLshHh7DUqWeq%2F325.jpg?alt=media\&token=dd64255c-f5e9-4b63-b8b6-863d114cebed)

ASP .NET Core Identity 只包含用户和角色，需要针对 Identity 做扩展，加上权限

UIprotron.Security.Core 负责管理资源和权限

UIprotron.Security.Identity 作为 Identity 的扩展，将资源和权限加入到 Identity 中，相当于一个适配层

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FKcPy7BVqb5N7gMrUzKEk%2F326.jpg?alt=media\&token=47a629db-038a-4d23-8e28-87d148b411bb)

UIprotron.Security.ActionAccess 和 UIprotron.Security.EntityAccess 分别负责 Action 和 Entity 的权限

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FJOaNJEO7aLs6fTcGtM0m%2F327.jpg?alt=media\&token=0e82878f-144f-4140-99f3-69054f43a6f1)

大体上分为以下几部分：

* ASP .NET Core Identity：用户认证的库
* UIprotron.Security.Identity：Core 与 Identity 的集成组件
* UIprotron.Security.Core：对资源和权限的管理
* UIprotron.Security.ActionAccess：Action 资源发现和权限控制
* UIprotron.Security.EntityAccess：Entity 资源发现和权限控制
* UIprotron.Security.Store.EntityFramework：资源和权限的 EF Core 持久层

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FebPvn1N66o8VgZnmcBaO%2F328.jpg?alt=media\&token=de9d337b-0d9f-443a-839e-130ab97f21aa)

**洋葱架构**

* CoreAdapters：最核心最稳定的放最里面
* Application Security.Identity：应用层，Identity 的扩展
* Action Access
* Entity Access
* EfResourceStore

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FIYyRA8Csj05FeWK4GRWI%2F329.jpg?alt=media\&token=4fd27ec5-9167-4f07-9146-a9ed431e0f73)

对每一层进行拆分，将功能拆分为 Core，Models，Store 和 EFStore

![](https://3083743005-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F8gwpNo3eyzHkX0O40HRA%2Fuploads%2FhiBCaYTypKsUbRh0BSuP%2F330.jpg?alt=media\&token=f09c00fb-e211-44ae-bb82-014d451f5c18)

end
