(文章基于 vue2.0 环境进行阐述,参考设计思路可适用于全部前端场景)
RBAC 模型
RBAC ,基于角色的权限访问控制(Role-Based Access Control):
RBAC的核心在于用户只和角色关联,而角色代表对了权限,是一系列权限的集合。
RBAC三要素:
- 用户:系统中所有的账户
- 角色:一系列权限的集合(如:管理员,开发者,审计管理员等)
- 权限:菜单,按钮,数据的增删改查等详细权限。 优点:
- 便于角色划分
- 灵活的授权管理
- 最小颗粒度授权
设计架构
权限分类
功能权限
访问权限:动态路由实现
菜单权限:基于路由表或者按钮极权限渲染
按钮权限:元素级操作权限
数据权限
接口请求操作,增删改查权限(本期需求)
权限优先级
通常来说【功能权限】>【数据权限】
举例说明:【浏览】/【点击】>【增】/【删】/【改】/【查】
基础架构图
按钮级权限架构
通常来说有两种实现方式:
- 基于角色对比方案,流程为:【操作模块需要的权限】-【权限中心】-【判断有无权限】-【是否有权限】 优势:
- 调用方便
- 单条件验证(只需传入功能需要的角色即可)
- 容易实现 劣势:
- 控制权完全交给开发人员
- 权限中心只能验证身份信息
- 管理分散,不易排查问题
- 不易与扩展
- 无法做到多条件验证
- 基于描述语句方案,流程为:【操作模块需要的权限】-【权限中心】-【判断有无权限】-【是否有权限】 优势:
- 统一管理
- 易与扩展
- 多条件验证
- 解耦,容易排查问题
劣势:
- 调用参数相对繁琐
- 调用依赖性强
- 新同学学习成本高
数据级权限架构
暂时由后端接口统一控制,后期如有必要前端也需要加限制
权限中心结构
基本元素
Effect
权限结果:允许(Allow)和拒绝(Deny)
Action
操作行为:get、add、update、delete、view等
Resource
操作资源:<module>:<relative>:<instance>
- Module ,资源所在模块
- Relative,资源功能,如果不支持可用 * 代替
- Instance,资源实例,如果不支持可用 * 代替 例如:
{
'resource': 'replay:play:dev_tools',
'action': 'use'
}
代表操作资源为【会话回放】-【会话播放页】-【开发者工具】的 use 权限
Condition
Effect 为 Allow 时的限制条件,可为多个 例如:
{
'effect': 'Allow',
'resource': 'projects/setting:members:role',
'action': 'update',
'condition': {
'Except': ['myself']
}
}
代表操作资源为【项目设置】-【成员管理】-【角色】的修改权限,但是不能修改自己
需求
原始需求表(举例)
功能模块 | 功能细分 | 操作权限 | 超管 | 管理员 | 分析师 |
---|---|---|---|---|---|
项目管理 | - | 查看 | ✔️ | ✔️ | ✔️ |
项目管理 | - | 创建 | ✔️ | ❌ | ❌ |
项目设置 | 成员管理 | 查看 | ✔️ | ✔️ | ✔️ |
项目设置 | 成员管理 | 编辑「」用户名 | ✔️「所有用户」 | ✔️「所有管理员和分析师」 | ✔️「仅自己」 |
项目设置 | 成员管理 | 修改或删除「」角色 | ✔️「除自己以外的其他用户」 | ✔️「除自己以外的其他管理员和分析师」 | ❌ |
需求翻译
补充说明:
- Either: 至少满足其中一项条件,才允许操作
- Exclude: 排除满足条件的操作
- Either 和 Exclude 任意一个字段存在,则必须进行校验
- 通常情况下 Exclude 和 Either 只存在一个
- Exclude 和 Either 如果同时存在,Exclude 优先级高于 Either
- 数据级现在由后端接口控制,后端同学需要关注
- 功能级由前端界面控制,前端同学需要关注
- 测试同学可根据此表格进行测试
落地实现
后端返回
请求方法
Get 请求
返回示例
{
"code": "00000",
"data": {
"Role": "superAdmin",
"resources": [
{
"id": 95,
"createAt": "0001-01-01T00:00:00Z",
"role": "superAdmin",
"resource": "projects:*:*",
"action": "add",
"exclude": ["myself"],
"either": [],
},
// ...
],
},
"message": "Success",
"meta": {
"time": "2022-03-10T10:43:33.251429786+08:00"
}
}
字段解释
前端调用
权限中心提供两个方法:
$permission
获取某项具体权限$checkRole
检查当前用户角色是不是需要的
(大多数情况下无需调用此方法,如$permission
无法满足,可以用此方法补充,99%的情况下$permission
方法已经能够满足)
方法实现
interface P {
userId?: Number, // 要操作对象的 userId
role?: String, // 要操作对象的 role
}
/**
* @description: 获取某项具体权限
* @param {*} resource 对应【 需求翻译 】表格中的 resource 字段,需要查看
* @param {*} action 对应【 需求翻译 】表格中的 action 字段,需要查看
* @param {*} payload 在【 需求翻译 】表格中存在 condition 时,需要传递 payload 参数
* @return {*} 结果
*/
Vue. prototype.$permission = async function(resource:string, action:string, payload: P): boolean {
... ...
}
/**
* @description: 检查当前用户角色是不是需要的
* @param {*} roles 需要检查的角色数组,比如需要超管权限则为 ['superAdmin']
* @return {*} 结果
*/
Vue.prototype.$checkRole = async function(roles: string[]):boolean {
... ...
}
参数说明
$permission
参数 | 说明 | 通俗理解 | 示例 | 必传 | 备注 | | ---------- | ------ | ---- | -------------------------------- | -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | resource | 操作资源 | 在哪 | projects_setting:members:role | 是 |<module>:<relative>:<instance>
组合,建议查阅【需求翻译】表格 | | action | 操作行为 | 干什么 | update | 是 | 建议查阅【需求翻译】表格 | | payload | 操作对象信息 | 操作谁 | { role: 'admin', userId: 123 } | 否 | | 在【需求翻译】表格中存在 condition 时,需要传递 payload 参数 涉及 myself 条件时,方法会强制校验 payload.userId 参数 涉及 superAdmin、admin 和 analyst 条件时,方法会强制校验 payload.role 参数$checkRole
参数 | 说明 | 通俗理解 | 示例 | 必传 | 备注 |
---|---|---|---|---|---|
roles | 角色数组 | 这个地方需要有哪些权限的人才能操作 | ['superAdmin', 'admin'] | 是 | 全部值:admin,superAdmin,analyst(超级管理员,管理员,分析师) |
调用示例
示例1
此处需要超级管理员或者管理员权限
// 此处需要超级管理员或者管理员权限
const result = await this.$checkRole(['superAdmin', 'admin']);
console.log(result ) // true 或者 false
示例2
在【项目设置】-【成员管理】,获取【查看】 权限
// 在【项目设置】-【成员管理】,获取【查看】 权限
const effect = await this.$permission(
'projects_setting:members:*',
'view'
);
console.log(effect) // true 或者 false
示例3
在【项目设置】-【成员管理】,获取修改某个用户【角色】信息的 权限
// 在【项目设置】-【成员管理】,获取修改某个用户【角色】信息的 权限,
// 这个用户的信息是:用户ID为 123 角色为 admin
const effect = await this.$permission(
'projects_setting:members:role',
'update',
{ role: 'admin', userId: 123 }
);
console.log(effect) // true 或者 false
进一步加工
指令,组件等等,不限制