🙆🏻‍♂️ 多角色系统设计 [Draft]

09-27 / 2023
PD

需求

有多个角色需要使用系统,通常情况下我们可以简单的分为前台和后台,比如售票系统中,前台给购票人员使用,后台给管理人员使用。

这是最简单的情况,现在事情变复杂了,售票系统需要给“代理商”使用,由他们代理售票,需要使用到额外功能,功能加在前台和后台都不合适,怎么办?

下面是两个方案,各有优缺点。

方案一、独立平台单独为角色提供功能

独立账号体系,独立后台。

优点:

  • 能够很好的隔离权限:对于相同的资源,代理商平台与后台平台能看到的资源,能使用的功能是不一样的。

缺点:

  • 大量重复开发工作:包括后端 API 和前端都有重复开发工作量。
  • 如果还有更多角色,比如招生系统中:“企业”,“老师”,“学生”,为每个角色单独开发系统是不现实的。
    • 也许不用为全部角色开发单独的系统,同样也只需要”前台“(给企业,老师,学生),“后台”给管理员使用。
    • 既然都能多角色公用一个系统了,那能不能就所有角色(包括管理员)都使用一个系统?

方案二、同一个账号,依赖权限分离菜单和权限

都公用一个账号,根据不同的权限显示不同的菜单功能,即代理商登录后台之后看到的只有代理商

优点:

  • 部分接口不用重复开发,可以重用,主要是账号体系可以共用。

缺点:

  • 权限隔离不方便做:比如同样是 更改用户 权限,在代理商系统中,可以编辑用户基础信息,但无法编辑账户余额;在后台管理系统中,可以编辑账号余额。要解决此问题需要将权限分为两个权限,即:代理商更改用户,后台更改用户。但如何按照这个逻辑分,那么系统中为了保险起见几乎所有功能都需要分离,有点心智负担。
  • 新注册的前台用户(比如 app 用户)却能登录后台,虽然此用户没有后台权限,登陆进来也是空的,但是这样并不能安全,因为后台 API 可能有权限漏洞。但如果区分了两个账号,至少管理员登录就能避免大多数安全问题。
  • 如果一个账号即是管理员又是用户,他想把账号借给其他人登录用户端,但是这个人却用来登录管理员端,这会导致很大的安全风险。

优化方案二:同一个账号但分不同“身份”

想要公用账号体系来减少后台 API 开发量。

为了解决上诉方案二的缺点,引入 “身份” 概念,通过 身份 隔离功能,并且一个身份一个后台,一个身份一个密码(可以相同)。

每个身份的权限严格分开,不公用,这是因为就算是同一资源,多个身份可以对其进行的操作也是不一样的(就算一开始是一样的,但无法预期之后迭代有不有改动,为了保险起见都分开)。

同样 API 由于绑定了权限与鉴权,所以不同身份需要请求不同的接口。

每个身份不能公用 token,切换身份需要重复登录,就算密码一致。

而前端可以做在同一个项目中,但需要为不同的角色做不同系统。

举例:

学生端,老师端,管理员端。

新注册的学生账号 10086,只有一个身份:学生,那么在登录的时候会校验身份,只能登录学生端。

有一天这个学生成为了老师,在注册老师端时 如果提交的是已有账号 10086,则只需要为这个账号新增一个老师身份。如果提交的是 10000,则重新注册账号并添加老师身份。

现在 10086 就可以同时登陆学生端和老师端了,并且可以独立更改密码。


单其实 “多身份” 依然有重复工作量(但比多账号好),依然需要思考如何分离身份。并且还必须根据业务来,不能万金油。

比如“老师”,“教授”,“机构”,“学生”。如果 “老师” 和 “教授” 功能差不多,则都应该给他们定为“老师”身份,同用同一个系统,仅仅通过权限来区别功能。

而“老师” 与 “机构” 几乎所有功能都不一样了,故需要分离为两个角色,使用两套权限区别功能。

比如 “项目管理员”,“管理员”,“合作商”。他们要使用功能都不一样,所以直接拆分为三个身份。


长桥 “经纪人”,“交易员”,“普通用户”,“管理员”,“券商”。其实只分了两个角色”前台用户“,”后台用户“。“经纪人”,“交易员”,“管理员”,“券商” 都属于后台用户,公用同一个账号,通过权限区分功能。

如果一个权限别公用在了两个身份上(如 代理商的更新用户和后台管理员的用户更新)则会导致跨身份操作,如何避免这个问题:

长桥中通过 ”应用“ 隔离权限,如在 WTT 中只能申请 WTT 的权限,不允许其他应用与 WTT 公用一个权限。

但这有一个问题:如果确实想要在 WTT,WAS(都是给交易员用的)之间公用权限则无法满足。

系统,身份?

要说服人选择一个方案而选择另一个方案最好的办法是说出方案存在的问题。

完全依赖权限隔离功能的问题?

安全问题:

假如在系统中有“老师”,“管理员”,“学生”三个角色,使用权限隔离功能,除了管理员能够给某某人添加权限之外,“老师”也可以为“学生”添加权限,这时候必须阻止“学生”被添加上后台权限,如 “更改管理员密码”。

为了阻止一个角色被添加上不能给此角色使用的权限,需要维护角色与权限的关系,简单的实现方式就是将“权限”分组,比如分成 “学生”,“老师”,“管理员”三组,学生账号只能被分配上学生组的权限,同时也需要一个字段来标识这个账号是 “学生账号”。

现在看起来没问题?

但“分组”只是理想情况,很多时候我们有公用的功能,如 “获取系统配置”,我们只为这个功能设计了一个权限,无论是老师还是学生都只要有这个权限都能使用这个功能。

公用权限会导致问题:

  • 某次迭代中需要让老师能获得的系统配置的内容与学生能获得的内容是不一样的,为了达到这个目的,就需要拆分为两个权限“老师获取系统配置”,“学生获取系统配置”来保证学生无法获取老师的系统配置,这个时候要兼容升级也变得十分复杂,需要人工为所有“老师”分配“老师获取系统配置”权限。这种公用权限的情况还普遍存在,为以后迭代带来了隐患,并且在添加功能的时候你还需要思考 要不要拆分为两个权限?费脑子。

为了解决这个问题,始终将老师和学生的权限分开一劳永逸,虽然带来了更多的开发量,但不费脑子。

现在再理一理,我们有两个需求:

  • 将老师和学生的权限严格隔离,不公用任何权限。
  • 账号上需要一个标识来说明这个账号是 “学生账号”、还是“老师账号”。

如何为两坨权限找一个合理的容器呢?那就是 “系统”。

表示是什么?“身份”。

总结出以下原则:

  • 一个账号需要具有系统对应的身份才能登录到某个系统中。
  • 一个身份只能在一个系统中使用,每个系统中的权限互相隔离,系统对账号的授权是发生在身份上的。
  • 一个账号如果需要在多个系统中使用,那么应新建系统对应的身份。

注意就算有多个角色需要使用同一个系统,那么他们也属于一个身份。

比如在 “师生系统” 中,老师和学生只是两个角色,而不是两个身份。不同角色通过权限来区分功能。

  • 当然也可以设计为 “老师系统” 与 “学生系统”,那么登录这两个系统的老师和学生就是两个身份了。
  • 如何拆分系统,需要结合两个系统之间的差异程度、安全问题、重复开发量来权衡(没有万能公式,只能不同业务系统不同看待,很费脑袋)。
    • 如果这只是一个短期项目,想快速上线,那么建议尽量不拆分过多的系统,拆分过多有更多工作量。
    • 如果是一个长期项目,需要考虑后续长远的迭代,那么可以尽量拆分来达到安全、可扩展的需求。

这个逻辑与 OAUTH 中,账号和 APP 差不多,用户只有一个账号(微信号),但是可以登陆不同的应用,不同应用之间的数据严格隔离。

能不能使用某系统,通过以下步骤判断:

  • 如果此系统开放注册,那么直接注册就行了,新增的用户无任何权限。
  • 如果此系统不开放注册,则需要其他管理员为此用户授权此系统(新增一条“账号与系统”的关联记录即可)

就长桥的例子,应该

“普通用户”:用户系统

“管理员”:管理系统

“券商”,“经纪人”:Whale 系统

“交易员”:交易系统

目前 经纪人 可以为交易员分配权限,可无法控制只能分配“交易员能使用的权限”,坏心人可以通过更改 AJAX 请求来为交易员分配超级管理员权限,并且通过 API 直接调用管理员功能,危害极大。

而不是

“普通用户”:用户系统

“管理员”,“券商”,“经纪人”,“交易员”:Whale 系统

© 2023 bysir's Blog - Hollow + Jsx / Mdx

GitHub