平台化服务的基石:隔离与交互策略模型

用户头像
孤岛旭日
关注
发布于: 2020 年 06 月 08 日
平台化服务的基石:隔离与交互策略模型

在《用户认证模型设计》、《权限模型设计》文章中我们介绍了用户权限服务的用户、认证、权限相关的模型,本文我们继续该服务模型的介绍,补充上最后一块核心模型。

模型回顾

之前我们提到租户(Tenant)时说它是数据隔离的单位,不同租户间的数据不能共享:

  • 账号(Account)隶属于租户,即便是同一个自然人在不同租户间也会有各自的账号

  • 应用(App)隶属于租户,权限体系既可以挂载到应用也可以到租户,但租户间权限不能互通

但这样的设定会导致无法实现需要租户间交互的场景,接下来我们就来讨论如何在 满足租户数据隔离这个大原则下 实现我们的需求。

租户账号关联

有应用A1隶属于租户T1,应用A2隶属于租户T2,现希望这两个应用实现单点登录。

这个需求场景细化后可分为两个:

  • 只有一方应用有账号,则另一方应该要先创建账号

  • 两个应用存在可关联的账号,则直接走账号关联

上图是租户账号关联的模型,模型比较简单,分别把两个租户的账号绑定在一起,但在实现流程上会比较复杂。需要把握的一个原则是:符合租户隔离模型。这意味着:

  • 安全,不同租户的账号还是隔离的,对应的认证、权限都独立成体系

  • 多凭证,不同租户下的用户认证后颁发的凭证(多为Token)不同,不能因为两个用户关联后使用同一个Token

上图是租户间用户关联最简化的版本,实际逻辑会复杂很多,需要说明的是统一登录页由用户权限服务提供,包含了H5、App Native等版本,该页面会存储不同租户下登录过的账号Token。

操作分离

为更好地描述接下来的需求,我们先对模型做一下优化。

这里增加了资源操作(Operation)模型,该模型用于表示对资源的不同类型操作,在之前的模型中它耦合在了权限(Permission)及应用权限(AppPermission)模型下,独立出资源操作模型后权限模型不直接与资源模型关联,而是通过资源操作模型间接关联资源,这样的模型职责更单一。

另外把原权限(Permission)模型重命名成角色权限(RolePermission)以更好地与应用权限(AppPermission)模型对应。

共享应用

很多公共服务(比如消息服务、流程中心)需要支持多租户,允许不同租户的账号登录并操作。比如:流程中心是公共服务租户下的一个应用,但不同业务方(不同租户)可以基于该中心配置流程,配置过程中需要获取自己租户下的用户信息。

由于租户隔离,应用只能操作当前租户下的账号,但如上所述,公共类型的服务却可能需要所有租户的信息,怎么办?

在以前的实践中笔者设计了“超级租户”模式,它是可以“凌驾”于一般租户之上,可自由地获取各租户的数据,这一模式下模型改动小,但这个设计改变了租户隔离模型,在安全性、实现复杂度都有问题,需要在模型及处理逻辑上打很多补丁。

所以之后“超级租户”设计指导原则为不能破坏即有的隔离机制,采用了一种更优雅的实现,本文进一步优化这一设计,将“超级租户”更细化到应用级,以“共享应用”替代“超级租户”。

要理解“共享应用”设计我们首先要明确的一点是用户归属问题。试想目前有两个租户:新零售租户和公共服务租户,公共服务租户提供了消息中心应用用于用户触达操作,此时新零售租户下的商城应用需要发短信功能,那么它要怎么在消息中心应用中操作呢?

在最初版的超级租户设计中,公共服务租户可以越权访问所有租户信息,所以模型上可能不用变更只需要在消息中心的处理逻辑上解除租户隔离即可。但前面说了这不是一个好的设计,我们讨论的是要符合隔离机制这一前提,那么最次的做法是把新零售租户下的用户copy到公共服务租户下即可实现需求,这一做法的最大问题在于用户归属争议:公共租户能不能持有所有租户的信息?笔者认为是不能的,这本质还是触犯了租户隔离机制。所以我们就会看到一方面电商应用需要操作消息中心,另一方面消息中心又拿不到新零售租户的账号,怎么破?

这时就需要我们转换思路:消息中心隶属于公共服务,但能不能让它“伪装”成其它租户?比如以新零售租户的用户登录,那么它就能以该登录用户所在的租户配置(角色、权限)操作?有了这个思路我们看下模型的变化:

我们在应用(App)模型上增加了是否是共享应用(shareApp)字段,在角色(Role)模型上增加了关联租户(relTenantId)、关联应用(relAppId)字段。同时添加了应用权限申请(AppPermissionApply)模型。

模型上做如下约定,当应用为共享应用(app.shareApp = true)时,它对应的角色也为公共角色(role.relTenantId = '' & role.relAppId = ''),这些角色可以为其它租户在申请通过后使用。这一约定是应用自愿开放角色及相关附属信息,调用需要申请审核,故符合租户隔离这一大前提。

我们看一下上面这个需求怎么实现:

  1. 系统应提供应用市场功能,罗列所有的共享应用

  2. 商城应用的管理员登录到应用市场,找到消息中心应用并发起使用申请

  3. 应用权限申请模型上添加一条记录:申请的新零售租户、商城应用、希望赋予管理员权限的账号A1、对应的消息中心应用,状态为审核中

  4. 消息中心的管理员登录到应用市场完成审核,状态改为审核通过,生成自定义首页

  5. 此时A1在自定义首页上登录消息中心,在此会话下消息中心以A1所在的租户、应用数据操作,实现上可以使用“Duck typing”的方式将凭证替换成操作者对应的凭证信息。由于本文关注的是模型,关于“Duck typing”凭证替换的实现不再展开,有兴趣的同学可以思考如下问题:消息中心的操作既涉及自己应用本身的权限又需要访问诸如用户权限中心的权限,如何将公共租户下消息中心自身权限数据与登录者所在租户及应用下的权限结合起来?



Tip:自定义首页用于快速确定当前访问者所属的租户及应用,用户也可以在正常的消息中心首页中手工选择对应的租户、应用,再输入了认证信息完成操作。

If it walks like a duck and it quacks like a duck, then it must be a duck

http://en.wikipedia.org/wiki/Duck_typing

共享服务

上一节讲了共享应用,是将整个应用共享出来,但有时候我们只想开放部分接口,比如:某个应用会开放诸如四要素认证、统一用户收货地址服务以供其它应用使用。

共享一个应用与共享应用内的某几个服务有明显的区别。在之前文章中我们提到了“应用鉴权”,本节我们展开讨论。

我们没有新增模型,只是在应用权限(AppPermission)模型中添加了申请状态(applyStatus)用于表示是否审核通过。当资源操作模型的关联租户(relTenantId)、关联应用(relAppId)字段为空时此操作视为共享服务。

设想有如下需求:新零售租户下的商城应用共享了“根据手机号获取对应的收货地址信息”这一服务(资源操作),保险租户的在线投保应用需要调用该服务。

共享服务实现上与共享应用类似:

  1. 系统应提供服务市场功能,罗列所有的共享服务

  2. 在线投保应用的管理员登录到服务市场,找到“根据手机号获取对应的收货地址信息”服务并发起使用申请

  3. 应用权限模型上添加一条记录:在线投保应用,状态为审核中

  4. 商城应用的管理员登录到服务市场完成审核,状态改为审核通过

  5. 此时在线投保应用就可以调用“根据手机号获取对应的收货地址信息”服务了

总结

至此,关于用户权限相关模型的讨论告一段落,这些讨论只是抛砖引玉,如何实现一个可扩展的、优雅的SAAS化用户权限体系是件很有意思的事情,值得每个架构师深思。

最后留几个思考点:

  1. 共享应用章节介绍的模型有个缺陷:无法实现不同应用的自定义授权。如商城应用和在线投保应用去申请消息中心应用的权限,给到的权限是对等的,无论实现消息中心模块1的权限给商城应用、模块2、3的权限给在线投保应用,但这种场景却又是存在的

  2. 资源(Resource)加上资源操作(Operation)用于定位一个操作,但这一模型下“资源”并不纯粹,比如有HTTP API“GET /user/{userId}”及“GET /mgr/user/{userId}”,两个API对应的角色不同,但它们对应的都是用户资源,目前的资源模型需要2条记录,怎么更优雅地实现?

  3. 目前讨论的模型都没有涉及安全加固,用户权限服务作为基础服务在实现上需要有更多安全方面的考量

  4. 本次涉及的权限模型包含了RBAC、ACL,在统一资源定位章节又涉及了部分ABAC,有没有简单、优雅的方案实现ABAC权限模型?



关注我的公众号:



发布于: 2020 年 06 月 08 日 阅读数: 59
用户头像

孤岛旭日

关注

努力成为一个好爸爸、好丈夫。 2018.10.30 加入

10年前,毕业时,芳华正茂,立志Coding完美世界,10年后,35,志向未成但理想依旧,勉励之、践行之。

评论

发布
暂无评论
平台化服务的基石:隔离与交互策略模型