# Admin.NET **Repository Path**: jovercao/Admin.NET ## Basic Information - **Project Name**: Admin.NET - **Description**: 🔥基于Furion/.NET 6实现的通用管理平台。整合最新技术,模块插件式开发,前后端分离,开箱即用。集成SqlSugar、多租户、缓存、数据校验、鉴权、事件总线、动态API、通讯、远程请求、任务调度、gRPC等众多黑科技。代码简洁、易扩展,让开发更简单、更通用、更流行! - **Primary Language**: C# - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 3725 - **Created**: 2022-09-14 - **Last Updated**: 2023-06-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 权鉴管理系统设计方案 ## 需求 ### 功能范围清单 - 单点登录系统 - 单点登录页 - 系统认证服务 - 管理后台 - 应用管理 - 功能及导航管理(树形) - 数据模型管理 - API管理 - 功能权限管理 - 数据权限管理 - API权限管理 - 应用DOTNET服务端SDK - 配置设置 - 身份验证中间件 - Auth API客户端 - Queryable数据过滤服务 - 权限描述Attribute集 - 开发文档 - 应用VUE客户端SDK - 配置设置 - 授权登录页 - 开发文档 ## 系统架构 ```mermaid flowchart TB; subgraph Auth authserver[server] authclient[web client] end subgraph App1 app1server1[server] app1client1[web client] end subgraph App2 app1server2[server] app1client2[web client] end ``` ## 权限体系设计 权限体系采用RBAC1 设计,具体可参考[RBAC权限模型](https://www.jianshu.com/p/115938c6294e),另外,为了更好的实现数据权限,还在其基础之上加添加了**机构**的参与,具体会后面进行介绍。 ### 权根分类 我们把权限分为2类: - 功能权限,功能权限又可以细分为以下类别 - 导航 - 页面 - 明细功能 - API权限 - 数据权限,数据权限包含以下类别: - 行权限 - 列权限 ### 权限依赖关系 为了避免权限冲突的情况产生,权限与权限间存在依赖关系,具体依赖关系如下: `明细功能权限` **依赖** `页面权限` **依赖** [`功能权限`, `导航权限`] **依赖** `API权限` **依赖** `数据权限` ### 授权方式 在本RBAC1权限体系里,一个用户可以拥有多个角色,权限赋予角色,用户绑定角色后,用户将拥有所有已绑定角色的权限。角色拥有自身多对多主从关系,一个从属角色可以重属于多个主角色,一个主角色可以包含多个从属角色,并且主角色拥有从属角色的所有权限。 为了降低数据权限控制的开发工作量和数据权限的用户配置工作以及规范权限管理,我们还在**RBAC1**的基础之上添加**机构**的参与,机构呈树形结构,用户从属于机构,然后权限体系通过数据权限分级,让机构参与当中。同时,为了使用该数据权限体系,我们还需要数据权限管理的业务数据表添加数据持有者ID字段,以便能分辨哪些数据的所有者。 **数据权限分级:** 我们将最常使用的数据权限进行了分组,共定义为6个**数据访问权限级别**: - 任意级别,可以访问系统任意数据,一般情况下平台超级管理员属于该级别,无须数据过滤 - 租户级别,可以访问用户自身所在租户下的任意数据,一般情况下租户管理员属于该级别 - 机构级别,可以访问用户自身所在机构下的所有数据,企业、部门、小组负责人属于该级别。 - 人员级别,仅可以访问用户自身的数据,一般情况下普通职员属于该级别 - 自定义,为了保留扩展性,添加了自定义级别,开发者可以实现一个过滤器或者定义一套过滤规则来实现完全自定义的数据权限分级 - 无权限,无数据访问权限 ### 多租户 本权限系统支持多租设计,如果应用需要实现多租户功能,可以自行实现租户级别数据过滤,如果不需要,则只要指定一个默认租户即可 #### 租户绑定 如果你的应用不支持多租户,可以设定一个默认租户 在一个应用支持多租户的情况下,并且一个用户本身拥有多租户时,通常需要对登录用户进行租户绑定,绑定的方式有以下几种: - 域名绑定(也是我们推荐的方式),需要应用前端在跳转到权鉴系统登录页是传递租户ID参数 - 在应用登录页面由用户选择,而后跳转登录页是传递到权鉴系统登录页中 - 直接在权鉴定系统登录页面由用户选择,而后身份验证时由权鉴系统返回给应用服务端 这需要应用在登录时明确所需要的租户,并在验证身份时指定租户ID,以便权鉴系统在后续获取正确的用户权限。 ## 名词与约定 | 名词 | 说明 | | ---------------------------------------- | ------------------------------------------------------------ | | AuthServer | 权鉴系统服务端 | | AuthClient | 权鉴系统客户端(浏览器) | | AppServer | 应用服务端 | | AppClient | 应用客户端(浏览器) | | AuthToken | 权鉴系统Token,AuthClient访问AuthServer时的Token | | AppToken | 应用系统Token,AppClient访问AppServer时的Token | | SessionKey | 用于AppServer向AuthServer请求用户信息的临时凭据,在AppServer使用完后销毁 | | CrgdSignatureAuthentication | 华润广东签名身份验证 | | 应用间身份验证-MutuallyAuthentication | 应用与应用之间的身份验证,这里包括子应用与权鉴系统之间的身份验证,这里统一使用CrgdSignature身份验证 | | 前端用户身份验证-FrontendAuthentication | 应用本身的身份验证 | | 权鉴系统身份验证 - UnifiedAuthentication | 权限系统本身的身份验证,针对用户端,默认使用JWT,也可以由应用自身指定 | | | | ## 数据结构 ### 功能项 功能项为对一个系统的功能的基本描述,功能以树形存储,包括: - 功能项 - ID - 功能类型:功能类型(功能组,外部URL,页面,页面操作,API,其他) - 功能名称: - 功能键值,唯一,功能ID,该值用于进行权限过滤 - URL,如果是页面或者外部URL,query参数可写在此处 - 是否显示在导航菜单,该值用于导航菜单过滤 - 父级ID,可空 - 功能项依赖 - ID - 功能项ID - 依赖功能项ID **【示例】:** - 系统管理(功能组) - 权限管理(导航分组) - 用户管理(页面) - 查询(明细功能) - 添加 - 修改 - 删除 - 功能权限 - 查询 - 添加 - 修改 - 删除 - ... - API权限(权限分组) - 用户API(权限分组) - 查询API - 修改API - 删除API - 启用API - 禁用API - 用户授权 - 更新用户授权API - 其他功能(功能分组) - 自定义功能1 - 自定义功能2 ### 数据项 - 数据模型 - ID - 模型名称 - 模型类型: 模型类型(系统模型,) - 模型 - 数据字段 - ID - 字段名 - 数据类型:数据类型(字符串,...,自定义) - ... - 是否拥有者字段 - 是否拥有机构字段 - 是否租户字段 - 数据字段访问级别 - ID - 字段ID - 访问权限级别 ### 用户 - 用户 - ID - 用户名 - 密码 - 姓名 - ... ### 角色 - 角色 - ID - 角色名称 - 摘要说明 - 角色成员 - ID - 角色ID - 用户ID ### 机构 - 机构 - ID - 机构名称 - 摘要说明 - 上级机构 - 机构成员 - ID - 机构ID - 用户ID ### 权限 - 角色功能权限 - ID - 角色ID - 功能项ID - 角色数据权限 - ID - 角色ID - 数据模型ID - 访问权限级别 ## UI ## API ### 用户端API #### 登录 略 #### 注销 略 #### Oauth签入 获取用户App端授权SessionKey 略 ### 应用服务端API #### 身份验证 ​ 权鉴系统应用API通讯,采用`CrgdSignature`身份验证,具体身份验证协议及SDK等使用方式请阅读[华润广东签名身份验证组件](https://gitlab.crgdpharm.com/crgd/component/crgd.authentication/-/blob/master/README.md)。权鉴系统为服务端,App应用服务为客户端,该文档中所说的`ClientId`,即为权鉴系统所分配的`AppKey`(注意,并非AppId),而SecretKey则为权鉴定系统所分配的AppSecret。 #### 验证登录 > POST /api/app/auth/checklogin **【Rquest】** ***body*:** ```json { "sessionKey": "...", // 用户在Auth接口中所取得的SessionKey } ``` ​ **【Response】** ***body:*** ```json { "success": true, "errMsg": null, "data": true // true 表示身份验证成功, false表示身份验证失败 } ``` #### 获取应用下用户权限 **【Request】** > GET /api/app/${appId}/user/${id}/permission **【Response】** ***StatusCode:*** 200 ***body:*** ```json { "success": true, "errMsg": null, "data": { "userInfo": { "userId": 4883589, //用户ID "userName": "admin", //用户名 "realName": "管理员", // 姓名", "nickname": "昵称", "avatar": "头像地址", "birthday": "", // 生日 }, // 当前登录租户 "tenant": { "tenantId": "租户ID", "tenantName": "租户名称" }, "orgnization": { "orgId": 3854895, // 机构ID "orgName": "智能与数字化部", "fullName": "华润广东 智能与数字化部", ... }, // 用户所属角色 "roles": [ { "roleId": 24595, "roleName": "系统管理员" } ], "navigations": [ // 格式待定 ], // 用户权限 "permissions": { "funcRights": [ { "funcId": 495959, "type": 1, // 功能类型 "key": "userMgr", "name": "用户管理", } ], "dataRights": [ { "modelId": 94959, "modelName": string, // 拥有以下用户ID所有权数据的访问权 "ownerUsers": [ 9549659, 123434, 546890, ... ] }, ... ] } } } ``` #### 授权过期通知 在用户身份登录过期时,会发送一个过期通知,应用端收到后自行处理。 #### 获取机构树 #### 获取角色列表 #### 获取用户列表 ### 设计时API 设计时API目的是为了提供理好的开发体验而设计提供,让用户可以在应用项目中维护功能项、数据项等种子数据。 #### 功能项及依赖更新 #### 数据模型更新 种子用户权限更新 ## 应用接入SDK ### 应用DOTNET服务端组件 #### 配置设置 #### Auth接入身份验证中间件 #### Auth API客户端 #### APP 身份验证服务API #### 权限管理器 #### 权限Queryable数据过滤服务 #### 权限描述Attribute集 ### 应用VUE客户端组件 #### 配置设置 #### 用户授权页面VUE组件 #### 多租户设置 用户登录时,可选定租户或者由应用服务端指定 ## 应用对接指引 ### 对接过程 - 应用申请,应用开发者向权鉴系统管理者提起应用申请,管理者为其添加应用,分配ID、访问密钥等工作 - 权限所需的数据准备: - 功能项 - 数据项 - 应用角色 - 角色权限 - 应用服务端开发(DOTNET) - 添加**权鉴系统应用DOTNET服务端组件**引用,并注入IoC容器,其中包含了登录服务等功能,api客户端 - 配置**权鉴系统应用DOTNET服务端组件**相关信息 - 将上一步准备的数据,编辑成组件所需要的JSON文件,并执行 - 为需要权限控制的API Controller/Action,添加相应的**Attribute** - 如果需要自定义数据过滤的,请根据数据权限级别自行实现 - 具体使用方法请参考[权鉴系统应用DOTNET服务端组件](#应用DOTNET服务端组件) - 客户端开发(VUE) - 如果客户端是VUE,安装**权鉴系统应用VUE客户端组件**npm包并引入,然后在Vue-Router中注册该组件访问路径并配置好相关配置,具体使用方法请参考[权鉴系统应用VUE客户端组件](#权鉴系统应用VUE客户端组件) - 联调测试 ### 单点登录流程示意图 - 获取菜单,并根据菜单数据展示菜单 - 获取API权限,并根据权限数据限制用户访问 - 数据权限,并根据权限数据限制用户访问 为了优化安全性,无论在权鉴系统还是应用中,我们均不使用Cookie保存身份凭据,对于应用端我们同样推荐这种做法,因此无法通过一次GET请求进行身份验证,相比标准oauth2流程会有以下修改: - AuthClient增加身份验证页 - AppClient增加身份验证页 - AuthClient登录验证时,不再是一次重定向GET请求完成,而是由AuthClient增加身份验证页加载时二次发起api请求服务端完成后跳转回AppClient增加身份验证页 - AppClient端登录验证时,亦是如此,验证页向AppServer发起登录请求,完成登录后跳转到初始访问页面 - 在auth2的基础上添加客户端授权页,用于接受SessionKey 正因如此亦导致程序变得比标准的oauth2更为复杂 ```mermaid sequenceDiagram AppClient ->>+ AppServer: 未登录情况下访问系统(最初访问页面) AppServer -->> AppClient: 返回401 未授权 AppClient -->> AuthClient: 重定向至权鉴系统单点登录页面 return_url指向AppServer授权页URL(附加:最初访问页面url) AuthClient ->> AuthServer: 如果未登录,输入用户名密码登录,如果已登录则跳过 AuthServer -->> AuthClient: 返回Token并存入本地存储 AuthClient ->> AuthServer: 通过本地Token向服务端获取SessionKey AuthServer -->> AuthClient: 返回SessionKey AuthClient -->> AppClient: 携带权鉴系统SessionKey跳转到AppClient授权页:"return url" AppClient ->> AppServer: 调用登录Login API,传递SessionKey AppServer ->> AuthServer: 通过SessionKey获取用户信息 AuthServer -->> AppServer: 返回用户信息(含角色、菜单、功能权限、数据权限)并缓存 AppServer -->>- AppClient: 登录成功,颁发AppServer端Token AppClient ->> AppClient: 跳转至最初访问页面 AppClient ->>+ AppServer: 获取导航菜单及功能权限 AppServer -->>- AppClient: 返回并展示导航菜单 AppClient ->>+ AppServer: 其他用户操作API调用 AppServer ->> AppServer: 根据缓存权限判断权限,并限制权限内数据 AppServer -->>- AppClient: 调用返回数据 AppClient ->>+ AppServer: 注销登录 AppServer ->> AuthServer: 用户注销 AuthServer -->> AppServer: 注销成功,销毁AuthToken AppServer -->>- AppClient: 注销成功,销毁AppToken,并清除用户权限缓存 ``` ### 对接工具 #### ASPNET SDK 对于使用aspnet体系的小伙伴来说,可以直接使用SDK进行对接,无需过多的了解实现细节,节省开发时间。 SDK详细文档,请看[应用接入SDK](#应用接入SDK) ## 议题 - [ ] 数据权限,绑定依据? - [ ] 方案1:绑定到功能项的明细功能中 - 优点1:可以针对每一个页面定义权限,方便最大程度的满足用户需求 - 优点2:相对于方案2,无须维护复杂的数据项体系 - 缺点1:每一个页面需要独立设置权限 - 缺点2:需要为不同页面中的同一种类数据独立定义功能项,在用户配置权限时,会增加一定工作量 - 缺点3:在应用端较难实现自动数据行权限过滤,往往需要硬编码实现 - 缺点4:字段过滤亦需要硬编码实现 - [ ] 方案2:绑定数据模型项中 - 优点1:应用端可以较为方便的实现数据行权限过滤,降低工作量 - 优点2:对于同一类数据,不需要重复定义数据项 - 优点3:可以较为方便的实现字段过滤 - 缺点1:需要维护数据模型表,但可以通过自动脚本维护 - 缺点2:为了实现勾选功能权限时,自动添加数据权限依赖,需要维护权限依赖管理,开发工作量增加 - 缺点3:开发周期较长 - [ ] 公司行政机构划分存在和真实管理职权不一致的情况,与权限所需要的机构不符,会导致数据权限无法得到正确执行,建议行政机构与权限机构分离。 - [ ] 数据权限数据较大,建议使用redis缓存处理