百度 ToB 垂类账号权限平台( 以下简称平台 ),是专注于为百度 ToB 垂类各产品线提供通用账号权限服务的基础平台,所提供的服务涵盖了租户管理、账号管理、单点登录、权限管控、账号安全、企业资质。业务方将账号、权限相关服务托管给平台后可以专注于自身的业务研发。目前已经接入了爱番番、爱采购、寻客宝、出海易一期产品线总计超过 1000万的企业账号,为各 ToB 垂类业务提供一站式账号与权限解决方案,以下是简化版的业务架构图:
平台业务架构
以下列出了必要的名词解释,便于更好地理解本文内容:
名词 | 解释 |
客户 | |
租户 | 一个客户可以有多个组织,单个组织即是租户,是最小的计费主体,具备相关营业执照,行业,法人,公司地址等自然存在的属性信息 |
账号 | 租户下的最小资源隔离主体,通过账号登录业务系统 |
主账号 | 租户创建时,由系统自动生成的超级管理员账号 |
子账号 | 主账号创建的隶属于同一租户的账号,可以指定任意角色权限 |
用户 | 泛指账号,可能为主账号或者子账号 |
角色 | 是一种虚拟身份,关联一组权限,必须关联到某个用户身份才能使用 |
功能 | 某个业务功能,通常是多个接口地址组成,比如打开某个菜单页面、按钮是否可见等操作 |
资源 | 资源是用户与系统交互的对象实体的一种抽象 |
RBAC | Role Based Access Control,基于角色的访问权限控制模型 |
租户版本 | SaaS产品的不同版本,比如基础版、专业版 |
BDRP | 百度分布式 Redis,为各业务提供一站式的高性能缓存&KV存储解决方案 |
平台支持一个客户在多个产品线注册多个租户,租户间的数据彼此隔离,每个租户默认创建一个主账号拥有最大的超级管理员权限,并生成具有管辖关系的租户组织结构树。主账号可以创建多个子账号,每个账号可以绑定多个第三方账号或者客户公司自己的账号体系( 比如微信、手机号、苹果账号、UC、passport 等 ),超级管理员可以给每个账号分配不同的角色从而具备不同的功能权限,比如给客服人员分配客服一线角色具备 IM 沟通权限。
企业组织与账号模型
2.2 稳定高效可配置的统一登录服务
ToB 垂类的业务场景不但需要支持 Saas 类业务,也要支持大客户私有化部署的业务,在设计的目标上,平台的账户登录服务需要给各类业务提供一个安全、稳定、高效、可配置的统一登录服务。
登录服务架构图
首先以账号密码登录为例,当用户进入爱番番的页面的时候,系统页面进行初始化,此时给当前的页面生成一套独有的加密信息和时长。这里加密信息会用于后续用户填写账号、密码等信息的传输过程当中的加密;时长是当前页面的时长,如果用户在当前页面静止的时间超过系统规定的时间,那么页面信息将会失效,系统会重新加载页面。当用户填写账号、密码、验证码等信息的时候,传输给系统一串加密信息,系统会在页面最开始加密生成的加密信息当中找到密钥,并进行解密,等完成基本的账号密码信息的校验后,会经过一系列的安全策略,比如非常用地登录、常用设备检查、手机验证检查等等。安全策略通过后会生成临时验证信息返回给前端,前端在进行校验的时候,系统会将临时的登录信息替换成真实的登录信息,从而系统完成了一次登录行为。
账号密码详细流程图
基于该思想可以抽象出一套模型,如下图,爱番番账号体系是可以支持多产品线的接入的,一个产品线归属多个账号。P 代表产品线,U 表示账号,E 代表登录服务的一些原子事件,C 代表登录相关的配置,S 代表登录相关的安全策略。一个产品线下可以组装不同的登录流程需要的原子事件,所以 P 和 E 的关系是一对多的情况。产品线 P 也可以有不同的配置,比如登录态时长、是否支持多端同时登录、登录端的个数等等配置。无论是产品线或者账号都会有一系列的安全策略的配置,当然这些安全策略可以是账号粒度也可以产品线粒度,比如对于一些黑产账号、异常登录行为账号的封禁策略,判断账号是否在常用地登录的提示策略等等。
配置扩展能力图1
通过将登录过程的步骤进行拆分,由不同产品线进行组织步骤可以实现企业的一些自定义的需求,但是如何支持不同的登录方式进行可配置操作?登录服务需要支持不同的登录方式,如密保手机登录、扫码登录、第三方登录等等主流通用的登录方式,在设计可配置流程的时候需要确保设计的通用性,将不同的登录方式的登录流程也能套用一套模型当中。抽象模型 M,表示登录方式,M 也是由不同的事件组成,而产品线P是可以自定义自己的产品需要哪些登录方式的,通过前端登录组件进行展示。当然为了考虑到安全性等方面的可配置,也设置了黑名单、白名单。黑名单可以基于账号级别的一些封禁,也可以基于异常 ip、手机号等进行封禁,而白名单往往和安全策略相关,比如一些跨域请求、跳转请求等等。下图就是基于上述一些考虑丰富后的登录服务配置扩展能力抽象模型。
配置扩展能力图2
基于设计的配置扩展的能力,可以将最开始的账号密码登录流程抽象成如下的登录可配置流程图,首先采用通用的加解密服务对请求的参数进行加密处理,然后各类的原子事件和对应的原子事件处理器都注册到中央处理器当中,通过中央处理器来进行组装流程,中央处理器会通过产品线和登录方式来查询数据库配置的事件编号顺序,然后逐步触发执行触发。
统一登录流程图
单点登录,一般应用于多个应用系统中,用户只需要登录一次就可以访问互相信任的应用系统。比如访问 http://a.baidu.com , 当登录之后,再次访问 http://b.baidu.com、http://a.aifanfan.com 都是可以免登录进行系统当中,用户只需一次登录,各个系统即可感知该用户已经登录。
单点登录对比图
基于上述对于单点登录的说明,可以想到只需要不同端认可一份登录认证凭据就可以实现,由于 http 是无状态的,自然可以想到 cookie 用于传递不同端的用户信息,但是由于 cookie 和域是强相关性,且基于浏览器有丢失的问题,所有在不同的端互通登录态使用 cookie 存在两个问题:一是跨域问题;二是 cookie 丢失问题。平台基于 CAS 协议设计实现一整套基于多端的单点登录流程,不但可以基于浏览器解决 cookie 问题,并且支持 native 各应用之间登录互通。
如下图是平台账号单点登录的主要流程图,在系统 A 使用 ToB 账号登录的时候,登录成功后,认证中心会生成一个临时认证信息 Stoken,并且会写入认证中心的域名 www.b.com 下 cookie 当中 Ptoken,当系统成功跳转系统当中,业务A服务端网关集成登录校验 SDK,由登录校验 SDK 将临时认证信息 Stoken 换取系统 A 的真实稳定的登录态,生成只属于系统 A 的登录校验 Atoken;当系统 A 跳转到系统 B 的时候,系统 B 服务端登录态校验发现当前域名下并没有登录信息,会执行 302 跳转到认证中心,由认证中心查找域名 www.b.com 下 cookie 当中Ptoken,通过认证后,会给系统B的请求接口返回临时 Stoken,由登录校验 SDK将临时认证信息 Stoken 换取系统B的真实稳定的登录态 Btoken。所以不同系统去认证信息的时候只需要关心系统本身域名存放的信息即可,如果登录校验失败或者 cookie 丢失的场景,会跳转到认证中心再次进行校验。
单点登录流程
Stoken : 服务临时 token,由认证中心去颁布;
Ptoken : 存放在认证中心服务端,会请求通过 Cookie 或者 Header 的 Key 值找到相对于的 Ptoken,从而拿到用户信息;
Rtoken : 业务方专属的 token,用于当前系统的登录态校验。
临时 Stoken 生成的时机有两种,一种是不同系统互相跳转的时候生成,一种就是登录的时候生成。不管哪种场景都是由认证中心去颁布,临时 Stoken 对应服务端存储的数据结构需要包含当前登录的账户信息、校验信息、客户端信息、加密信息。在设计上是一种 Hash 的结构方式,临时 Stoken 存储各类的信息保持 key-value 的方式,这样读取、存储都清晰方便。当业务系统设置登录完成跳转后的地址是 http://a.baidu.con/index ,那么生成临时 Stoken 后,登录服务 SDK 会将 Stoken 追加到跳转地址上,变成 http://a.baidu.con/index?st=xxxxx ,由于临时 Stoken 暴露在 url 上,为了保证安全性,临时 Stoken 的有效期设置 30s 以内,如果 30s 当中,临时 token 没有被业务请求进行认证,则直接进行销毁。
当业务 A 登录后,业务请求经过登录 SDK 校验的时候,会拿到请求参数上的Stoken,通过解密到真实存储的 Key,拿到当前登录的账户信息、校验信息、客户端信息、加密信息,生成稳定 Rtoken,将 Stoken 的信息复制到 Rtoken,并通过产品线的配置设置登录态时间。这一步就是临时 Stoken 换取真实登录态Rtoken。
登录态校验示意图
通过之前的登录流程可配置、单点登录流程的介绍,在实际处理过程当中,将服务器的 session 划分成三个部分,分别是登录时期的 session、临时 session、登录态 session 。登录时期的 session 控制总体登录流程的时间,防止恶意狂刷,这种的 session 时长要设置分钟级别,如果 session 过期,需要前端登录 SDK 重新加载初始化页面信息,重新开始登录流程;临时 session 就是为了单点登录设计,这种 session 时长设置秒级别;真实登录态 session 为用户最终的登录session,存在真实的登录 Token 信息,通过不断的交互 session 来保证登录流程的安全性。如下是账号密码登录划分图。
分阶段session划分
加解密过程在登录流程比较重要,也是为了安全性,将具体的加密算法进行替换。这里面使用对称加密算法 A 、非对称算法 B,账号密码加密算法 C、D 等。
加解密流程图
首先通过加密算法A生成一对秘钥对,将公钥给端上使用,端上随机数 Random 结合公钥算法 A 加密当前随机数 Random 传给登录服务。
首先 Random 将解密出来,然后生成新的密钥对,然后将私钥,公钥存放 session 里面,并返回算法 B 加密后的公钥,这个公钥的 salt 是解出来的原始的 Random 参数。
端上算法 B 解密后出真实的公钥后。将密码算法 A 加密。
密码校验的时候,会在 session 里面取之前存的私钥,解出原始密码,再通过算法 C、D 加密后和数据库比对。
cookie属性
请求校验
验证码
验证码是非常有必要的,而验证码的目的就是区分正常人和机器的操作。验证码的作用在于区分人和机器,防止被暴力破解,提高破解密码的难度。只是不同阶段表现形式不同,未来的趋势是更加智能无感知,用户体验更好。登录服务支持图片验证码、人机交互滑动验证码、手机验证码。业务方可以根据安全要求不同接入不同的验证码的方式。
ToB 多租户账号服务支持客户相关需求,有时候会和外部企业进行合作,需要保证客户体验的一致性,打通外部平台和账号系统的账号授权关系,保证登录爱番番系统的客户可以免登录到外部系统。因此需要形成一套完善的授权体系。OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数据。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌( token ),用来代替密码,供第三方应用使用。
第三方发起授权登录请求,当查询相关应用注册到授权中心的时候,如果用户没有在账号系统进行登录,会默认链接到提供的统一授权登录页,登录完成后会拉起应用或重定向到第三方网站,并且带上授权临时票据 code 参数;
通过 code 参数加上 AppID 和 AppSecret 等,通过 API换取access_token;
OAuth时序图
功能鉴权交互图
功能鉴权包含两部分,分别为升级版RBAC模型即 TD-RBAC、权益包模型,下面分别进行介绍。
在介绍GD-RBAC之前,先介绍下基础的RBAC模型,Role Based Access Control,基于角色的权限控制模型的核心是在用户和权限之间引入了角色的概念。避免用户和权限的直接关联,通过用户关联角色、角色关联权限的方法来间接地赋予用户权限,从而达到用户和权限解耦的目的。
RBAC模型模型是20世纪90年代研究出来的一种新模型,RBAC认为权限授权的过程可以抽象地概括为:Who是否可以对What进行How的访问操作,并对这个逻辑表达式进行判断是否为True的求解过程,也即是将权限问题转换为What、How的问题,Who、What、How构成了访问权限三元组。即在RBAC模型里面,有3个基础组成部分,分别是:用户、角色和权限。RBAC通过定义角色的权限,并对用户授予某个角色从而来控制用户的权限,实现了用户和权限的逻辑分离,极大地方便了权限的管理。
用户(User):每个用户都有唯一的UID识别,并被授予不同的角色;
角色(Role):是一种虚拟身份,关联一组权限,必须关联到某个用户身份才能使用;
资源(Resource):资源是用户与系统交互的对象实体的一种抽象;
功能(Function):某个业务功能,通常是多个接口地址组成,比如打开某个菜单页面、按钮是否可见等操作;
RBAC模型
RBAC 支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则
最小权限原则:RBAC 可以将角色配置成其完成任务所需的最小权限集合;
责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务;
数据抽象原则:可以通过权限的抽象来体现;
上面介绍了基础的 RBAC 模型,但随着业务发展,无法满足不同产品线的复杂鉴权需求,比如爱番番转化提升域需要实现同一个账号在不同线索池下拥有不同的操作权限,基于此我们提供 GD-RBAC 模型:
GD-RBAC 模型是在 RBAC 基础上进行提升,具体升级点为:
1、增加用户组,多个用户( 可以为1个 )可以聚合为一个用户组,同一用户组内的用户角色相同,进而具备相同的权限,可以简化用户配置流程;
GD-RBAC模型
有了 GD-RBAC 模型,我们就可以很简单的实现用户在不同线索池下的权限配置,如下图所示:
同一个账号可以关联到不同的维度即线索池,然后每个线索池可以关联不同的角色,从而可以帮助业务实现复杂的业务定制鉴权。
GD-RBAC模型在线索池业务的应用
有了 GD-RBAC 是不是就能实现所有功能权限管控了呢?仍然存在以下问题:
①、版本差异:爱番番存在多个售卖版本,比如线索管家基础版、拓客标准版、拓客专业版、私有部署版,版本之间的权限范围是不一致的,那么GD-RBAC下的不同租户的同一个超级管理员角色对应的权限必然是不相同的,举例来说基础版的管理员角色是不具备标准版管理员的潜客定投权限的;
②、功能增购:同一个用户同一个版本,用户可以单独购买产品实现功能增购,此时即使没有升级版本但权限也得到了扩充;
权益包模型
权益包是可定制的业务属性,租户可以关联多个权益包,比如爱番番产品线存在租户版本权益包、400 电话功能权益包、用户坐席 license 权益包等分类。通过租户关联权益包来实现租户权限的最大集合。通过权益包模型可以很灵活、简单的解决上述提到的三个问题:
①、租户版本权益包解决版本差异:比如租户版本权益包可以分别定义不同版本的最大功能集合,用户购买不同版本即具备了权益包的所有权限;
②、功能权益包解决功能增购问题:将不同产品功能打包成单独的功能权益,用户通过购买功能实现权限的升级。
完整的功能鉴权
基于 GD-RBAC 和权益包模型,平台可以快速支持业务各种权限使用场景,这里举例客服代操作场景:
先说下代操作的概念:一组用户可以访问、操作另一组用户拥有的资源。典型例子:具有管辖权限的客服使用客服账号进入客户的爱番番系统,帮助客户完成系统配置。
通过模型快速支持客服代操作业务
除了以上我们介绍了功能鉴权之外,不同产品线有比较灵活的数据鉴权需求,比如不同部门、职位的账号如何实现数据权限的隔离或管辖?如何构建可定制的数据鉴权能力?我们定义了用户特征,分为通用特征和自定义特征:
通用的维度特征诸如维度、产品线、行业等每个 ToB 产品都具备的,我们通过维度来实现通用特征,比如每个部门、岗位都是不同的维度。业务方可以通过维度模型规范用户数据,支持特征级联,构建用户的特征网络,从而支持不同业务自定义不同的用户特征模型;
数据权限抽象
数据维度模型
左右值树在组织结构与职位的应用示例
那么为什么要采用左右值树存储维度呢?先介绍下左右值树基本原理:
使用左右序号对每一个节点进行标记,根节点的左值是树的最小序号,根节点的右值是树的最大序号,且高度是0。
查询某节点的所有后代节点: 任意节点,其后代的左值一定比当前节点左值大,后代的右值一定比当前节点右值小
查询某个节点的祖先: 任意节点,其祖先的左值一定比当前节点左值小,其祖先的右值一定比当前节点右值大
计算节点下有多少子节点:包含自身:(右值 - 左值 + 1)/ 2;不含自身:(右值 - 左值 - 1)/ 2
下图是将维度1-组织结构通过左右值树进行标记的图,其中 lv 表示树的层高,lleft 表示左边序号,lright 表示右边序号,通过左右值树进行存储标记,查找用户挂在哪个维度节点上,查询效率高。
左右值树节点排序
最终我们可以通过两个维度节点的交集查询得到账号张三所在部门、职位的管辖结果集:
维度管辖交集计算
所以通过左右值树的特点,对于查多写少的数据场景来说,可以快速的获取节点的所有子节点,进而高效的获取管辖关系判断数据权限。
一级缓存:优先通过内存查询权限配置信息,其中考虑到内存信息时效性,内存信息有效期默认为5分钟;
二级缓存:如果内存失效或者查不到,则从redis中查询权限信息;
DB兜底:如果redis查询为空可能代表有权限变更导致的redis失效,会从DB查询并刷新到redis,通过超时配置进行db访问超时熔断。
高性能高可用的鉴权服务
本文主要介绍了百度 ToB 垂类账号权限平台的账号登录服务以及权限服务的设计:
通过配置化的事件编排灵活支持各产品线丰富的登录方式和登录策略,详细展开介绍了单点登录的设计与实践,并通过分阶段 session 控制与多种手段保障登录服务的安全可靠;
结合一个示例完整的介绍了通过 GD—RBAC 和权益包模型解决复杂业务的功能鉴权,并通过多维度鉴权灵活实现数据权限治理,助力业务和客户简单便捷的完成权限管控。
未来,平台将持续丰富账号和权限的能力,持续提升服务稳定性,秉持“技术为产品服务”的理念提供一站式账号与权限解决方案帮助 ToB 垂类各业务线更好的发挥业务产品价值。
本篇系百度 MEG ToB 垂类爱番番鲁班团队多位同学共同编写:
Corwin:资深研发工程师,先后负责多个业务方向,擅长平台和业务系统架构设计,用技术解决业务痛点;
Tao:高级研发工程师,负责业务基础、运营支撑等相关业务,擅长分布式与系统设计。