权限
根据不同的用途,系统提供4种权限,分别是功能权限、界面权限、API 权限和数据权限。
权限 | 用途 |
---|---|
功能权限 | 控制功能树上显示哪些页面,用户能访问哪些页面,作用于门户的功能树和页面 |
界面权限 | 控制用户能访问页面上的哪些组件(按钮、输入框、表格列、标签页等),作用于前端页面 |
API权限 | 控制用户能访问哪些服务,调用哪些后端的API接口,作用于请求 |
数据权限 | 控制用户能访问哪些数据集的 CRUD 操作,能访问某个数据集的哪些字段, 能访问哪些数据范围(比如查看自己、查看部门、查看公司层级的数据),以及控制关联查询的访问,作用于数据库代理 DBProxy |
所有的权限都存储在企业门户应用的权限表 permission 中,表结构见下表
角色 ID | 权限编码 | 权限名称 | 条件 | 类型 | 状态 |
---|---|---|---|---|---|
authc | *:/react/mobileapp/mobile/org-auth/org.w:get | 组织 | menu | 1 |
- 权限编码
- 采用三段式设计,使用冒号作为分隔符,将命令划分为类型、操作、属性/对象三个层级。支持使用通配符进行设置,即::*为最大权限
- 功能权限的权限编码: *:页面路径:get
- 界面权限的权限编码: *:页面路径#界面权限标识:*
- API 权限的权限编码: *:请求路径:请求方法
- 数据权限的权限编码: *:UUID:sql
- 条件
- 数据权限的具体配置内容
- 类型
- 功能权限是 menu、界面权限是 ui、API权限是 service、数据权限是 sql
权限项
以 API 为例,一个 API 在未声明为权限项时,默认注册用户可访问。在声明为权限项后,只有有权限的用户才能访问。
权限项在服务元信息文件中的 authorize.permissions 中声明,例如
{
"authorize": {
"permissions": [
{
"id": "_react_mobileapp_mobile_org-auth_org.w",
"code": "*:/{serviceName}/mobileapp/mobile/org-auth/org.w:get",
"name": "组织",
"type": "menu"
}
]
}
}
特别说明
企业门户应用架构中的权限服务,支持设置最低权限要求,默认为注册用户 authc,可以改为匿名用户 anonymous。表示未声明为权限项的内容,匿名用户皆可访问
功能权限
功能权限用于控制功能树上显示哪些页面,以及用户能访问哪些页面
这两者在页面是否声明为权限项时,有一些区别,见下表
权限项 | 功能树 | 打开页面 |
---|---|---|
页面未声明为权限项 | 没有权限不显示 | 没有权限能打开 |
页面已声明为权限项 | 没有权限不显示 | 没有权限不能打开 |
实现原理
功能权限是对页面这个层级访问的控制,可以限制用户是否能访问这个页面。可以通过新建角色并分配功能权限来开启对本页面的访问。
数据库表写入
新建页面后,默认会在门户的数据库表中自动插入1条本页面的访问权限,但未指定角色。角色分配权限后,会自动新插入1条本页面的访问权限,并同时指定角色。
开发时设置写入的是门户的permission_debug表,运行时设置写入的是permission表。
其中code会描述是访问哪个页面文件,name为用户自定义的名称,type为menu(指功能权限),role_id如果未分配给角色则为空。例如:
访问约束
- 前端约束
当配置好权限,并分配给角色、授权给用户后,在门户显示页面菜单时,会判断登录的用户是否拥有该访问权限,如果不存在,则页面菜单不会显示出该页面。
- 后端约束
如果用户绕过页面菜单,直接访问页面的链接,则后端会进行判断并提示访问无权限,避免绕开前端的漏洞。
实现效果
以功能权限页面为例,功能权限功能会自动加入该页面权限,如下图所示:
设置权限后的访问效果如下:
- 有页面访问权限
- 无页面访问权限
门户菜单没有出现“功能权限”页面。
- 绕过页面菜单直接访问页面链接
页面上会提示页面权限不足。
界面权限
界面权限是对页面上的组件这个层级访问的控制,可以限制只读或者隐藏相关组件,组件包括页面上加入的所有组件,比如文本、按钮、输入框、表格列、标签页等。
使用本功能需要从组件市场下载“界面权限组件”,并把界面权限组件、上下文组件这2个组件同时加到页面中。
实现原理
数据库表写入
新建界面权限后,默认会在门户的数据库表中自动插入1条权限,但未指定角色。角色分配权限后,会自动新插入1条同样的权限,并同时指定角色。
开发时设置写入的是门户的permission_debug表,当微服务注册到门户后,会写入到permission表中。
其中code会描述是哪个页面文件的权限标识,比如权限标识是gnqxymtabledisplay。name为用户自定义的名称,type为ui(指界面权限),debug_type为insert,role_id如果未分配给角色则为空。例如:
界面权限判断
当前端页面放置了界面权限组件后,平台会根据用户是否有授权的界面权限,来控制页面组件的只读、隐藏属性。
以以下配置为例,页面会根据权限标识(gnqxymtabledisplay),找到组件(tableColumn2),如果访问的用户没有该权限,则该组件设置为prop属性(hidden或者readonly),例如:
<item comps="tableColumn2" compsname="tableColumn2" id="default21" op="oneOf" permissions="gnqxymtabledisplay" permissionsname="功能权限页面表格列显示权限" prop="hidden"/>
实现效果
界面权限配置如下:
访问效果如下:
- 有访问权限
- 无访问权限
具体表现为:
1、输入框和操作按钮为只读,操作不了;
2、发布者这列被隐藏了;
3、标签页中的“有权限用户可见”这个页签被隐藏了;
API 权限
API权限是控制用户能访问哪些服务,调用哪些后端的API接口。作用于后端。
实现原理
数据库表写入
新建服务后,默认会自动插入1条本服务的访问权限,但未指定角色。角色分配权限后,会自动新插入1条本服务的访问权限,并同时指定角色。
开发时设置写入的是门户的permission_debug表,运行时设置写入的是permission表。
其中code会描述是访问哪个服务,name为平台自动生成的名称,type为service(指API权限),debug_type为insert,role_id如果未分配给角色则为空。例如:
访问约束
当用户访问服务时,页面会向后端发起服务请求(权限code描述的路径,比如/permissionandrolecase/main/service/weather),请求会根据API权限判断用户是否拥有该权限,如果无权限,则请求返回异常,提示无权限。
实现效果
以天气预报服务为例,API权限功能会自动加入该服务权限,如下图所示:
设置权限后的访问效果如下:
- 有访问权限
- 无访问权限
数据权限
数据权限是控制用户能访问哪些数据集的CRUD操作,能访问某个数据集的哪些字段(列权限),能访问哪些数据范围(行权限,比如查看自己、查看部门、查看公司层级的数据),以及控制关联查询、子查询的访问。
同时数据权限具备一些高级特性的能力,比如设置为“单独使用的列权限”、“只有返回的列在权限指定的列范围内时才应用”等。
数据权限作用于后端。
数据集CRUD访问权限
通过数据权限可以控制对数据集的查询、插入、更新和删除(CRUD)等操作的访问限制。
实现原理
数据库表写入
新建权限后,默认会在门户的数据库表中自动插入1条权限,但未指定角色。角色分配权限后,会自动新插入1条同样的权限,并同时指定角色。
开发时设置写入的是门户的permission_debug表,运行时设置写入的是permission表。
其中code是数据权限的标识,name为用户自定义的名称,type为sql(指数据权限),debug_type为insert,role_id如果未分配给角色则为空。例如:
_condition记录了该权限具体配置的内容,比如这里为CRUD的全部权限,没有限制访问字段和条件。其中S指查询数据,I指插入数据,U指更新数据,D指删除数据。截图如下:
访问约束
当用户访问前端页面的数据内容时,页面会向后端发起dbrest请求,请求会根据数据权限判断用户是否拥有该权限,如果无权限,则请求返回异常,提示无权限。
实现效果
CRUD的操作限制既可以单独设置(比如只能查询数据),也可以多选组合设置(比如可以查询数据和删除数据)。参考如下图所示:
设置访问限制后,在向后端发起请求时返回是否有权限的结果:
同时页面上也会提示异常:
数据集字段访问权限(列权限和行权限)
实现原理
数据库表写入
配置完权限后,会在数据库表写入相关内容。其中code是数据权限的标识,name为用户自定义的名称,type为sql(指数据权限),debug_type为insert,role_id如果未分配给角色则为空。例如:
_condition记录了该权限具体配置的内容,比如这里为信息表CRUD的全部权限,同时设置了访问字段和条件。
其中S指查询数据(比如限制只返回createTime,person,title,type等列字段),I指插入数据(比如限制插入type等于财务公告),U指更新数据(比如限制更新type等于财务公告,同时限制where type=财务公告),D指删除数据(比如限制where type=财务公告)。截图如下:
访问约束
当用户访问前端页面的数据内容时,页面会向后端发起dbrest请求,请求会根据数据权限判断用户是否拥有该权限,如果无权限,则请求返回异常,提示无权限。
实现效果
数据集字段的访问权限,可以设置以下几类:
- 某些字段不返回或者不能插入/更新
- 插入数据时可以判断插入的值是否符合要求
- 更新数据时可以判断此次更新是否可以操作,更新的值是否符合要求
- 删除数据时可以判断此次删除是否可以操作
设置访问限制后,在向后端发起请求时返回是否有权限的结果:
同时页面上也会提示异常:
数据范围访问权限(行权限)
通过数据范围访问的设置,可以约束用户可以访问哪些数据范围(比如查看自己、查看部门、查看公司层级的数据)。这里通过列权限以及“可用的表达式”的设置来实现。
实现原理
可用的表达式
常用的几个表达式有:
表达式名称 | 表达式 |
---|---|
用户标识 | ${/uaa/userinfo#user_id} |
用户主部门 | ${/context/opm/currentusermainorg?type=dpt#fid} |
用户主机构 | ${/context/opm/currentusermainorg?type=ogn#fid} |
用户岗位 | ${/context/opm/currentusermainorg?type=pos#fid} |
当前日期 | @{CurrentDate()} |
可以通过表达式来设置 where 条件,比如 销售人员ID 等于 ${/uaa/userinfo#user_id},这样就可以限制登录用户只能查看自己录入的订单信息。
特别说明
- 系统提供的关于用户的可用表达式,都是调用 API 获取返回值。同理,也可以调用开发者自定义的 API 获取返回值
数据库表写入
配置完权限后,会在数据库表写入相关内容。写入的关键内容主要是conditions属性,例如:
访问约束
当用户访问前端页面的数据内容时,页面会向后端发起dbrest请求,此时dbrest会根据权限设置改写sql语句,把where条件加上来限制用户访问的内容。
实现效果
以销售订单为例,普通员工只能查看自己的订单,不能查看其他人的订单;部门领导可以查看部门员工的订单;公司领导可以查看整个公司员工的订单。
录入销售订单数据时,需要录入销售人员ID为上下文组件的获取当前人员成员主键(),销售部门FID为上下文组件的获取当前部门主键全路径()。
权限设置方式如下:
- 普通员工只能查看自己的订单
查询条件设置为:销售人员ID = 用户标识。
- 部门领导可以查看部门员工的订单
查询条件设置为:销售部门FID 包含 用户主部门。
- 公司领导可以查看整个公司员工的订单
查询条件设置为:销售部门FID 包含 用户主机构。
实现效果如下:
- 普通员工查看的数据范围
技术2是普通员工,只能看到技术2自己录入的订单。
- 部门领导查看的数据范围
技术1是技术部的部门领导,可以查看整个技术部人员录入的订单。
- 公司领导查看的数据范围
总经理是公司领导,可以查看整个公司人员录入的订单。
关联查询访问权限
在前端页面的数据集,设置关联查询后,join的表也会受到数据权限的约束。
LEFT JOIN
left join权限影响规则:
1、左表会同时受到左表列权限和行权限的影响;
2、右表只受到右表列权限的影响,不受行权限的影响(也就是where条件不起作用)。
以销售订单为例,销售订单的客户ID关联客户表,销售订单 left join 客户(客户为右表),设置客户表的查询数据权限(列只返回客户名称,且客户姓名需等于张三),则查询到的客户信息只有客户名称这个列,且客户只有张三。
客户表(右表)查询权限设置:
关联查询结果(没有返回客户手机号码这列,且客户姓名没有过滤张三这个条件):
RIGHT JOIN
right join权限影响规则:
1、左表只受到左表列权限的影响,不受行权限的影响(也就是where条件不起作用);
2、右表会同时受到右表列权限和行权限的影响。
以销售订单为例,销售订单的客户ID关联客户表,销售订单(左表) right join 客户(右表),设置销售订单(左表)的查询数据权限(只返回订单编码、销售部门和销售人员),设置客户表(右表)的查询数据权限(列只返回客户名称,且客户姓名需等于张三)。
销售订单(左表)查询权限设置:
客户表(右表)查询权限设置:
关联查询结果(销售2登录,但左表并没有过滤销售人员ID这个条件):
INNER JOIN
inner join权限影响规则:
1、左表会同时受到左表列权限和行权限的影响;
2、右表会同时受到右表列权限和行权限的影响。
同样以上边的设置为例,inner join 查询后的结果(左、右表都启用了过滤条件):
子查询访问权限
在前端页面的数据集,设置子查询后,子查询的表不会受到数据权限的约束。
以销售订单为例,设置了子查询为客户表,设置如下:
同时客户表设置查询数据权限:
客户表的数据权限只返回客户姓名(并没有返回主键),同时需要过滤客户姓名为张三,但返回的结果显示,子查询并没有经过数据权限的约束,如下图所示:
高级特性
单独使用的列权限
数据权限设置为“单独使用的列权限”,设置此属性目的是为了扩大列字段的权限范围。
以查询数据为例,如果存在多个查询权限,列字段默认会做交集返回。如果勾选了其中1个权限的“单独使用的列权限”,此时多个权限的列字段会做并集返回,扩大了返回的列字段范围。
使用场景
在一些使用场景中,有些用户角色需扩展原来权限返回的列字段,但又不想更改原来的权限,此时可以新建1个权限,把这个权限设置为“单独使用的列权限”,再授权给这些用户角色,这样可以添加返回的列字段了。
例子如下:
权限1列字段 | 权限1单独使用的列权限设置 | 权限2列字段 | 权限2单独使用的列权限设置 | 授权效果的列字段(并集) |
---|---|---|---|---|
客户姓名 | 否 | 手机号码、客户状态 | 是 | 客户姓名、手机号码、客户状态 |
实现效果
新建”单独使用的列权限“的查询数据权限前的访问效果:
新建后的访问效果(多了手机号码和客户状态的返回):
注意事项
设置该属性时需要注意以下情况(不能全部权限同时设置该属性),避免使用异常:
权限1列字段 | 权限1单独使用的列权限设置 | 权限2列字段 | 权限2单独使用的列权限设置 | 授权效果的列字段 |
---|---|---|---|---|
客户姓名 | 是 | 全部列字段 | ||
客户姓名 | 是 | 手机号码、客户状态 | 是 | 全部列字段 |
只有返回的列在权限指定的列范围内时才应用
数据权限设置为“只有返回的列在权限指定的列范围内时才应用”,设置此属性目的是为了降低行权限的使用范围,只有在满足返回的列在权限指定的列范围内时,行权限才生效。
使用场景
在一些使用场景中,查看某些列字段的时候,需要设置过滤条件,如果查询的列字段不满足要求,就不需要这些过滤条件。
例如,用户要查看客户的手机号码,只看状态为有效的客户就可以了,状态无效的不需要查看。设置如下:
权限列字段 | 权限行权限 | 是否“只有返回的列在权限指定的列范围内时才应用” |
---|---|---|
客户姓名、手机号码、客户状态 | 客户状态 等于 1 | 是 |
案例说明如下:
案例 | 查询的列字段 | 是否满足权限设置的列字段要求 | 结果 |
---|---|---|---|
案例1 | 客户姓名、客户需求 | 不满足 | 客户状态 等于 1 不生效 |
案例2 | 客户姓名、手机号码、客户状态 | 满足 | 客户状态 等于 1 生效 |
案例3 | 手机号码、客户状态 | 满足 | 客户状态 等于 1 生效 |
实现效果
在前端页面的数据集组件中,可以设置需要返回的列字段:
返回的列字段不满足权限设置要求的访问效果:
返回的列字段满足要求的访问效果(启用了客户状态 等于 1的过滤条件):