写点什么

Worktile 权限设计与实现

发布于: 2021 年 04 月 28 日
Worktile 权限设计与实现

作者:Worktile 后端开发工程师霍世杰 @阿杰


Worktile 是国内最优秀的企业级项目协作与目标管理工具之一,这个项目已经持续了 9 年之久,书写了研发团队的历史长卷,我作为“后来者”有幸地参与其中。在过去研发的一年里,做的事情大多数是对原有功能的增强和重构,也学习和总结了 一点点 Worktile 核心技术和知识,本文就是其中之一—— 权限系统。


Worktile的权限异常复杂,在开发中,从疑惑到深入,再到后来的望之止步,直至最终的克服,这其中多次与同事的交流,又多次的总结,逐渐地清晰,到现在对它进行了我认为比较全面的总结,下面就跟大家一起 揭秘(分享) 这块复杂而精彩的内容。

一、Worktile 帐户体系





首先总览一下 Worktile 的帐户体系。在此之前,先介绍一下该系统:它是多租户系统的一种,在我接触过的多租户系统中,有两种类型,一种是像企业微信,飞书,它们的租户类型有用户和团队/组织/企业,那么这类应用的帐户体系 就有两个用户概念,一个是系统用户,一个是团队成员;而另一种就是 Worktile,租户类型 是 企业/团队/组织的,它是不含个人租户,一切业务的基础条件是 团队(team) ,所以权限这块也是基于 团队成员来控制的。


目前比较主流的权限设计模型,一种是 ACL(Access Control List),是主要是基于用户来控制权限,而另一种是 RBAC 模型(Role-Based Access Control )基于角色的访问控制,而这两种在 Worktile 中都有涉及,在绝大部分是 RBAC 模型,少量的权限是基于用户设计和控制的。


上图中,可以看到 Worktile 权限是由功能权限和数据权限两部分组成,下面对这功能权限和数据权限两部分 具体讲解。


二、设计与实现


功能权限和数据权限都是基于应用的维度来划分的,Worktile 现有的应用有项目、任务、目标(OKR)、消息、日历、审批、网盘、考勤...... (更多了解点 这里)。

功能权限

设计

功能权限是完全按照 RBAC 模型 设计的,关系为:





由两部分组成:操作权限和可见权限,是按照应用模块的维度划分的,每个应用模块下分布多个权限点和可见范围。





给用户呈现的形式是在应用的后台—>角色管理中(见下图),其中企业角色包含两部分: 1. 系统默认角色(所有者、管理员、成员、部门主管) 2.自定义角色,权限列表(下图右侧)中,打对勾的代表该角色已拥有的权限,数据范围 代表 该角色 在 应用内的可见维度。


  • 可操作权限





  • 可见权限





实现


在传统关系型数据库的设计,基本都是三张表:角色表,权限表,角色权限关联表,如果校验一个或一组权限,是需要三表关联查询的。


而在 Worktile 中则不然,采用的储存方式是:非关系型数据库 Mongodb + 系统配置文件, 由一张表来表现,权限列表由系统配置文件存储。

  • Mongodb,天然支持数组和 JSON 类型的数据储存,在角色和权限的关联配置中更为灵活,这一环在此设计中,不可或缺!

  • 配置文件主要存储的是权限列表,系统内置,前端也需要一份配置是因为列表展示位置匹配。


为何这么设计?


数据库是因为整个产品的主库就是 Mongodb,这也是最大的原因;权限列表之选择配置文件储存,我猜想 是因为权限是系统内置,并且是固定的,改动频率较低,所以没有必要每次都要再交互一次数据库,不仅是功能权限如此,下面谈到的数据权限亦是如此。

系统的权限列表

配置的数据结构大致如下(以下只是说明结构,并非实际数据):

{  task: {      display_position: 100,      create: {	  	key: "create_task",        display_position: 100,        storage_position: 1,        admin_default: 1      }  },  module1: ...  module2: ...  module3: ...}
复制代码


一级节点的 key 代表的是应用模块,二级节点的 key 代表权限点,value 为权限的描述,详细如下:


角色与权限的关联配置


在 Worktile 中只需要一张角色表足矣!结构如下(关键字段): id,name,privileges,is_system,其中核心字段是 privileges,该字段记录的是权限项,是一个 json,详细如右下图。





privileges 中一级节点是应用模块,value 是角色拥有的权限值,该值是以 0 和 1 组成的字符串组成,其中 1 代表拥有,0 代表未拥有,每个字符排列的位置是由配置文件中的 storage_position 字段;scope 是可见范围。





使用


下面基于上述实现对以下三种常见场景的使用:

  • 获取角色下所有的权限列表 以及 拥有状态(后台管理)

  • 获取一个成员的所有权限(登录后前端需要储存的用户权限)

  • 校验一个企业成员是否拥有某个权限(API 请求对成员的鉴权)


ps: 只举例一种,剩下两种类似。





三、数据权限


看完前面 功能权限的实现 大家应该觉得非常的简单,如果下面还抱着这种心态,那可就真是小看它了,在此 我也希望大家 能够在下面仔细阅读,体会其中的设计,文章较长,有不明白或需要讨论的欢迎评论留言。


在数据权限的设计中,各行各业大多不尽相同,这是由业务来决定的,Worktile 的业务就非常的复杂,所以 权限也随之复杂,Worktile 中应用模块很多,由于篇幅原因不会在文中全部讲解,只挑一些典型给大家分享一下。


我这里以 Worktile 核心模块 mission(项目、任务) 来讲解分析,mission 是非常值得一说的,精彩在于能够允许用户 高度自定义配置,模块下的角色和权限都是可配置的(这里说明一下非上述系统角色和权限)。


由于业务的复杂,不得不在这里给大家从一些界面截图进行演示,有疑惑的地方欢迎提出。


项目(Project)

概览

项目是任务的集合,项目分解出多个任务,项目中有组件(以不同维度 对 任务 的展示和管理)





项目权限的分为可操作权限和可见权限。可操作的权限





对应的页面





可见权限


规则为:公开项目所有成员可见,私有项目只有已加入的成员可见。


  • 可见范围





  • 私有项目已加入的成员





到现在大家应该对项目和项目中的权限有了大体概念和认知,下面详细分析。

设计





项目权限的设计中有角色模式、项目角色、权限三部分,这三者关系大致为:





配置方面,有全局配置和项目内配置,全局配置的维度是全部的项目,项目设置是全局配置的延伸,对单个项目的配置,项目的配置来源于全局配置。


  • 角色模式 角色模式可以理解为角色组,在项目中必须拥有一种角色模式,由系统默认角色模式和用户自定义创建 两部分组成(见下图)。

    全局配置

    


  • 项目中配置(这里只能选择全局配置好的角色模式)

项目角色项目角色有系统内置角色(管理员、普通成员、只读成员) 和用户自定义角色(见下图)

全局配置


项目中配置


  • 项目权限在概览中,已经介绍权限列表了,下面只展示配置和关联。


  • 全局配置(角色关联权限)


  • 项目中配置


从上面的设计中可以看出,其实这块的权限还算简单,下面从技术角度讲解一下具体的实现。


项目权限的实现是由三张表和一个配置完成的,分别是项目表(Project)角色模式(RoleMode)角色(Role) 权限列表的配置文件(PrivilegeConfig)

关系:





  • 一个项目可以配置多个角色模式,一对多,绑定字段在主表(Project) 的 role_modes。

  • 每个角色模式可以创建多个角色,一对多,绑定字段在 从表(Role) 的 role_mode_id。

  • 每种角色可以配置多个权限,关系是一对多,只不过这里的权限字段(permission_value) 是由 0 和 1 组成的 字符串,其中 0 代表 未拥有,1 代表 拥有。


项目的可见权限是通过项目两个字段来控制的,一个是 visibility(公开/私有),另一个是 members(项目成员),当项目是公开的,所有成员可见,当项目是私有的,只有成员列表存在的才可见。使用:


  • 如何获取权限列表?服务端读取系统配置文件,返回给前端,前端再映射对应位置展示即可。

  • 如何获取角色对应的权限列表?服务端读取角色表找到对应 权限值(由 0 和 1 组成的字符串),然后根据系统配置进行排序,返回给前端,前端通过 0 1 值排列和勾选。

  • 如何验证成员的权限?1. 可见权限,首先判断 visibility 是公开还是私有,如果是公开项目所有成员可见,如果是私有其次判断 当前成员是否存在于项目成员 2. 如果验证的是操作权限,从项目表(Project)读取该成员的 id 和角色,再根据角色找到权限值(由 0 和 1 组成的字符串),最后根据系统配置文件对应权限的位置映射权限值的具体单个值,如果是 1 那么就拥有此权限,反之未拥有!

任务(Task)

概览


任务是工作内容的具体表现形式,任务可以对工作内容描述,流程控制,任务和任务之间的关联和派生。


ps:任务可以单独存在,也可以存在于项目中。


下面是实际场景中的使用







设计


任务的权限和项目的权限从设计上就有很大的不同,任务的权限 是由权限主导的,模型见下图。





其中涉及角色模式、角色、权限模式、权限、安全级别模式、安全级别等概念,​ 安全模式和安全级别等同于 可见权限





而其中与项目共同的部分是 角色模式不同之处是任务角色是由项目角色和任务角色( 任务负责人,参与人等) 组成,而主要设计方式是通过 配置任务类型权限来控制任务的权限,换一种说法讲给任务配置权限,不如说给任务类型 配置权限,这两者是可以划等号的

什么是任务类型?


见名知意,就是任务的所属类型,之所以 Worktile 适用于大多数企业的原因也是核心就是任务类型,同时也支持企业成员创建和自定义。


实际中使用的任务类型





注:任务 有且只能 拥有一个 任务类型

可操作权限
  • 权限模式权限模式是多个权限的集合,也可理解为权限组,由系统内置权限模式(通用权限模式、软件权限模式等) 和用户自定义权限模式(见下图)。

    全局配置

    


  • 项目中任务类型的权限配置



  • 权限模式 与 角色模式





  • 权限模式、角色模式与角色的关联





  • 权限模式、角色与权限的 关联





权限


  • 系统内置权限:系统定义不可更改的属性权限,比如创建任务,归档任务,拷贝任务......等

    任务属性权限:任务的属性是支持用户配置的,所以属性拥有 属性权限,比如修改负责人,修改任务状态......

可见权限

任务中的可见权限,称为“安全级别”。由安全模式和安全级别两部分完成。


  • 安全模式(系统内置安全模式和自定义安全模式)



安全模式与角色模式安全模式与角色模式的绑定,表示该安全模式下哪些角色模式的角色可见。



  • 安全模式与安全级别





  • 安全级别与角色表示哪些角色(包括 项目角色和任务角色)可见





  • 项目中任务类型与安全级别的配置




实现

任务的权限涉及 9 张表,3 个配置文件。





表及关系:

  • Task 任务表、TaskType 任务类型表,一对一的关系,关联字段在主表 Task 的 task_type_id。

  • PermissionMode: 权限模式表,与任务类型是多对一的关系,一个任务类型有多种权限模式。

  • TaskPermission:任务权限字典表,是通过系统配置文件(system task permissions) 初始化到该表中的,与权限模式 的关系是多对一,一个权限模式对应多个权限项,关联字段 selectable_permission,注意该字段是同步到 权限模式下,后续供角色来选择。

  • RoleMode: 角色模式表,与权限模式是一对一的关系,一个权限模式绑定一个角色模式。

  • Role: 角色表,与角色模式表是多对一的关系。

  • PermissionRole: 权限角色表,与权限模式是多对多的关系,该表的权限必须是从权限模式下的权限项 中选择,角色必须是该权限模式下绑定的角色模式下的角色中选择。

  • SecurityMode: 安全模式表,配置的是任务的可见权限,是从系统配置文件初始化到数据库中,与角色模式是一对一的关系,一个安全模式绑定一个角色模式。

  • Security: 安全级别表,配置的是任务角色(含任务内置角色和默认角色模式下的角色)的可见权限,也是 从系统配置文件初始化到数据库中的,与任务角色是一对一的关系,与安全模式是多对一的关系,一个安全模式下可以配置多个角色。


使用逻辑:

检查一个成员拥有任务的操作权限





检查一个成员是否对一条任务有可见权限





四、总结

文中所讲权限整体是按照 RBAC 模型设计


权限列表均采取 配置文件的方式储存,好处是轻量,减少连表复杂操作,较少的与数据库交互


文中 mission 模块的数据权限:

  • 项目可操作权限是角色组 + 角色 + 权限

  • 项目可见权限绑定是成员

  • 任务的权限是权限组 + 权限 + 角色组+ 角色来控制任务属性


非常感谢能够认真阅读到这里的伙伴,如果文中有不明白或更好的意见欢迎交流,期待更多揭秘,还望持续关注 Worktile!


欢迎进入官网注册Worktile

发布于: 2021 年 04 月 28 日阅读数: 29
用户头像

还未添加个人签名 2021.02.01 加入

还未添加个人简介

评论

发布
暂无评论
Worktile 权限设计与实现