百度 ToB 垂类账号权限平台的设计与实践
一、前言
百度 ToB 垂类账号权限平台( 以下简称平台 ),是专注于为百度 ToB 垂类各产品线提供通用账号权限服务的基础平台,所提供的服务涵盖了租户管理、账号管理、单点登录、权限管控、账号安全、企业资质。业务方将账号、权限相关服务托管给平台后可以专注于自身的业务研发。目前已经接入了爱番番、爱采购、寻客宝、出海易一期产品线总计超过 1000 万的企业账号,为各 ToB 垂类业务提供一站式账号与权限解决方案,以下是简化版的业务架构图:
△平台业务架构
以下列出了必要的名词解释,便于更好地理解本文内容:
二、账号登录服务
2.1 企业组织与账号模型平台支持一个客户在多个产品线注册多个租户,租户间的数据彼此隔离,每个租户默认创建一个主账号拥有最大的超级管理员权限,并生成具有管辖关系的租户组织结构树。主账号可以创建多个子账号,每个账号可以绑定多个第三方账号或者客户公司自己的账号体系( 比如微信、手机号、苹果账号、UC、passport 等 ),超级管理员可以给每个账号分配不同的角色从而具备不同的功能权限,比如给客服人员分配客服一线角色具备 IM 沟通权限。
△企业组织与账号模型
2.2 稳定高效可配置的统一登录服务
ToB 垂类的业务场景不但需要支持 Saas 类业务,也要支持大客户私有化部署的业务,在设计的目标上,平台的账户登录服务需要给各类业务提供一个安全、稳定、高效、可配置的统一登录服务。
△登录服务架构图
作为企业应用的登录服务,为了保证企业用户的安全性,在流程上的设计上增加了很多安全的策略,所以总体的登录流程会较复杂:
首先以账号密码登录为例,当用户进入爱番番的页面的时候,系统页面进行初始化,此时给当前的页面生成一套独有的加密信息和时长。这里加密信息会用于后续用户填写账号、密码等信息的传输过程当中的加密;时长是当前页面的时长,如果用户在当前页面静止的时间超过系统规定的时间,那么页面信息将会失效,系统会重新加载页面。当用户填写账号、密码、验证码等信息的时候,传输给系统一串加密信息,系统会在页面最开始加密生成的加密信息当中找到密钥,并进行解密,等完成基本的账号密码信息的校验后,会经过一系列的安全策略,比如非常用地登录、常用设备检查、手机验证检查等等。安全策略通过后会生成临时验证信息返回给前端,前端在进行校验的时候,系统会将临时的登录信息替换成真实的登录信息,从而系统完成了一次登录行为。
△账号密码详细流程图
那么作为 saas 服务的统一业务基础,我们需要在登录服务上解决几个问题:
如何支持登录服务可配置化?可配置化能力可以支持不同的企业在私有化部署的场景可以按照企业自身的业务来定制登录流程,也可以支持爱番番 saas 服务接入不同的产品线的定制化需求。
如何支持多产品线应用接入并支持单点登录?
如何保障账号登录安全?登录服务的核心就是保证登录功能的安全性,作为爱番番、爱采购等系统的一级服务,防止系统被恶意入侵,登录流程的安全性就变得非常的重要。
2.2.1 配置扩展能力一个复杂的问题通常是有多个小问题组合起来,而处理一个复杂问题也会涉及到多个不同的处理步骤,只需要采用分而治之的方式,将复杂的处理逻辑拆分出不同的小的步骤,这样可以将问题简单化。所以核心的处理思路也是将登录流程不同的处理步骤拆分出来,将步骤对应行为和操作。这样就可以得到多个不同的原子行为,而每一种行为对应不同的原子操作。
2.2.1.1 模型抽象基于该思想可以抽象出一套模型,如下图,爱番番账号体系是可以支持多产品线的接入的,一个产品线归属多个账号。P 代表产品线,U 表示账号,E 代表登录服务的一些原子事件,C 代表登录相关的配置,S 代表登录相关的安全策略。一个产品线下可以组装不同的登录流程需要的原子事件,所以 P 和 E 的关系是一对多的情况。产品线 P 也可以有不同的配置,比如登录态时长、是否支持多端同时登录、登录端的个数等等配置。无论是产品线或者账号都会有一系列的安全策略的配置,当然这些安全策略可以是账号粒度也可以产品线粒度,比如对于一些黑产账号、异常登录行为账号的封禁策略,判断账号是否在常用地登录的提示策略等等。
△配置扩展能力图 1
通过将登录过程的步骤进行拆分,由不同产品线进行组织步骤可以实现企业的一些自定义的需求,但是如何支持不同的登录方式进行可配置操作?登录服务需要支持不同的登录方式,如密保手机登录、扫码登录、第三方登录等等主流通用的登录方式,在设计可配置流程的时候需要确保设计的通用性,将不同的登录方式的登录流程也能套用一套模型当中。抽象模型 M,表示登录方式,M 也是由不同的事件组成,而产品线 P 是可以自定义自己的产品需要哪些登录方式的,通过前端登录组件进行展示。当然为了考虑到安全性等方面的可配置,也设置了黑名单、白名单。黑名单可以基于账号级别的一些封禁,也可以基于异常 ip、手机号等进行封禁,而白名单往往和安全策略相关,比如一些跨域请求、跳转请求等等。下图就是基于上述一些考虑丰富后的登录服务配置扩展能力抽象模型。
△配置扩展能力图 2
2.2.1.2 统一登录流程基于设计的配置扩展的能力,可以将最开始的账号密码登录流程抽象成如下的登录可配置流程图,首先采用通用的加解密服务对请求的参数进行加密处理,然后各类的原子事件和对应的原子事件处理器都注册到中央处理器当中,通过中央处理器来进行组装流程,中央处理器会通过产品线和登录方式来查询数据库配置的事件编号顺序,然后逐步触发执行触发。
△统一登录流程图
2.2.2 单点登录 2.2.2.1 流程介绍单点登录,一般应用于多个应用系统中,用户只需要登录一次就可以访问互相信任的应用系统。比如访问 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 丢失的场景,会跳转到认证中心再次进行校验。
△单点登录流程
2.2.2.2 认证逻辑在流程介绍当中可以了解到单点登录当中设计了三种票据 Stoken、Ptoken、Rtoken :
Stoken : 服务临时 token,由认证中心去颁布;
Ptoken : 存放在认证中心服务端,会请求通过 Cookie 或者 Header 的 Key 值找到相对于的 Ptoken,从而拿到用户信息;
Rtoken : 业务方专属的 token,用于当前系统的登录态校验。
生成 Stoken 临时 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 没有被业务请求进行认证,则直接进行销毁。
Stoken 换取 Rtoken 当业务 A 登录后,业务请求经过登录 SDK 校验的时候,会拿到请求参数上的 Stoken,通过解密到真实存储的 Key,拿到当前登录的账户信息、校验信息、客户端信息、加密信息,生成稳定 Rtoken,将 Stoken 的信息复制到 Rtoken,并通过产品线的配置设置登录态时间。这一步就是临时 Stoken 换取真实登录态 Rtoken。
校验登录态过程
△登录态校验示意图
2.2.3 登录安全性保障 2.2.3.1 分阶段 session 控制通过之前的登录流程可配置、单点登录流程的介绍,在实际处理过程当中,将服务器的 session 划分成三个部分,分别是登录时期的 session、临时 session、登录态 session 。登录时期的 session 控制总体登录流程的时间,防止恶意狂刷,这种的 session 时长要设置分钟级别,如果 session 过期,需要前端登录 SDK 重新加载初始化页面信息,重新开始登录流程;临时 session 就是为了单点登录设计,这种 session 时长设置秒级别;真实登录态 session 为用户最终的登录 session,存在真实的登录 Token 信息,通过不断的交互 session 来保证登录流程的安全性。如下是账号密码登录划分图。
△分阶段 session 划分
2.2.3.2 敏感信息加解密加解密过程在登录流程比较重要,也是为了安全性,将具体的加密算法进行替换。这里面使用对称加密算法 A 、非对称算法 B,账号密码加密算法 C、D 等。
△加解密流程图
加解密过程如下:
首先通过加密算法 A 生成一对秘钥对,将公钥给端上使用,端上随机数 Random 结合公钥算法 A 加密当前随机数 Random 传给登录服务。
首先 Random 将解密出来,然后生成新的密钥对,然后将私钥,公钥存放 session 里面,并返回算法 B 加密后的公钥,这个公钥的 salt 是解出来的原始的 Random 参数。
端上算法 B 解密后出真实的公钥后。将密码算法 A 加密。
密码校验的时候,会在 session 里面取之前存的私钥,解出原始密码,再通过算法 C、D 加密后和数据库比对。
2.2.3.3 其他安全举措
cookie 属性
Secure:安全性,指定 Cookie 是否只能通过 https 协议访问,一般的 Cookie 使用 HTTP 协议即可访问,如果设置了 Secure(没有值),则只有当使用 https 协议连接时 cookie 才可以被页面访问。
HttpOnly:如果在 Cookie 中设置了 “ HttpOnly “ 属性,那么通过程序 ( JS 脚本、Apple t 等 ) 将无法读取到 Cookie 信息,防止 xss 攻击。
时长:设置有效期,建议设置时长不宜过大。
请求校验
域请求限制:登录相关的接口支持跨域的场景,但是需要校验请求头 origin 属性,设置白名单,只有在登录认证中心配置的来源 origin 才可以放行;对一些执行 302 跳转的请求,同样设置白名单的控制跳转请求。
频次:登录相关的请求会根据当前的 IP、浏览器唯一 ID、客户端设备 ID 等信息设置时间范围内频率的限制,如果超过阈值,会进行频次报警,也会对指定的 IP、设备、账号进行及时的封禁。
验证码
验证码是非常有必要的,而验证码的目的就是区分正常人和机器的操作。验证码的作用在于区分人和机器,防止被暴力破解,提高破解密码的难度。只是不同阶段表现形式不同,未来的趋势是更加智能无感知,用户体验更好。登录服务支持图片验证码、人机交互滑动验证码、手机验证码。业务方可以根据安全要求不同接入不同的验证码的方式。
2.3 OAuth 能力
2.3.1 OAuth 介绍 ToB 多租户账号服务支持客户相关需求,有时候会和外部企业进行合作,需要保证客户体验的一致性,打通外部平台和账号系统的账号授权关系,保证登录爱番番系统的客户可以免登录到外部系统。因此需要形成一套完善的授权体系。OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数据。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌( token ),用来代替密码,供第三方应用使用。
2.3.2 授权流程在用户授权登录已接入 OAuth2.0 的第三方应用后,第三方可以获取到用户的接口调用凭证( access_token ),通过 access_token 可以进行对外提供的 OpenApi 接口进行调用,从而可实现获取爱番番账号用户基本开放信息和帮助用户实现基础开放功能等。目前平台 Oauth 能力支持 authorization_code 模式,该模式整体流程为:
第三方发起授权登录请求,当查询相关应用注册到授权中心的时候,如果用户没有在账号系统进行登录,会默认链接到提供的统一授权登录页,登录完成后会拉起应用或重定向到第三方网站,并且带上授权临时票据 code 参数;
通过 code 参数加上 AppID 和 AppSecret 等,通过 API 换取 access_token;
通过 access_token 进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。
△OAuth 时序图
三、权限服务
作为 ToB SAAS 产品的基础权限服务,服务于多个产品线,需要为来自各行各业的客户解决复杂多变的权限管控需求,提供包含功能权限和数据权限管控的能力。
3.1 问题与思路问题 1:各产品线在代码中实现复杂权限控制逻辑,业务权限理解成本高、维护成本高;
解决思路:提供易于接入的可扩展的功能鉴权、数据鉴权能力;
问题 2:权限逻辑复杂、性能要求高,随着业务发展和客户量的增加,系统不稳定风险高;
解决思路:建设高性能高可靠的鉴权服务;
3.2 复杂的功能鉴权 3.2.1 权限接入方式如下图所示,权限服务包含登录态校验、权限校验能力,并提供三种鉴权接入方式,分别为:
①、通过网关统一集成鉴权 sdk 实现鉴权,包含登录态的校验、权限校验;
②、不经过网关,单独引入鉴权 sdk 实现鉴权;
③、直接通过 RPC 接口与权限平台交互,实现鉴权;
△功能鉴权交互图
功能鉴权包含两部分,分别为升级版 RBAC 模型即 TD-RBAC、权益包模型,下面分别进行介绍。
3.2.2 GD-RBAC 模型(引入用户组的基于维度和角色的权限访问控制模型)3.2.2.1 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):某个业务功能,通常是多个接口地址组成,比如打开某个菜单页面、按钮是否可见等操作;
权限(Auth):角色和功能之间的关联映射;
△RBAC 模型
RBAC 支持三个著名的安全原则:最小权限原则、责任分离原则和数据抽象原则
最小权限原则:RBAC 可以将角色配置成其完成任务所需的最小权限集合;
责任分离原则:可以通过调用相互独立互斥的角色来共同完成敏感的任务;
数据抽象原则:可以通过权限的抽象来体现;
3.2.2.2 GD-RBAC 上面介绍了基础的 RBAC 模型,但随着业务发展,无法满足不同产品线的复杂鉴权需求,比如爱番番转化提升域需要实现同一个账号在不同线索池下拥有不同的操作权限,基于此我们提供 GD-RBAC 模型:
GD-RBAC 模型是在 RBAC 基础上进行提升,具体升级点为:
1、增加用户组,多个用户( 可以为 1 个 )可以聚合为一个用户组,同一用户组内的用户角色相同,进而具备相同的权限,可以简化用户配置流程;
2、在用户组与角色之间增加维度,同一个用户组可以关联 ≥ 1 个维度,在每个维度下可以单独关联唯一的角色,进而实现同一用户在不同业务场景下的不同权限;
△GD-RBAC 模型
GD-RBAC 示例有了 GD-RBAC 模型,我们就可以很简单的实现用户在不同线索池下的权限配置,如下图所示:
同一个账号可以关联到不同的维度即线索池,然后每个线索池可以关联不同的角色,从而可以帮助业务实现复杂的业务定制鉴权。
△GD-RBAC 模型在线索池业务的应用
3.2.3 权益包模型有了 GD-RBAC 是不是就能实现所有功能权限管控了呢?仍然存在以下问题:
①、版本差异:爱番番存在多个售卖版本,比如线索管家基础版、拓客标准版、拓客专业版、私有部署版,版本之间的权限范围是不一致的,那么 GD-RBAC 下的不同租户的同一个超级管理员角色对应的权限必然是不相同的,举例来说基础版的管理员角色是不具备标准版管理员的潜客定投权限的;
②、功能增购:同一个用户同一个版本,用户可以单独购买产品实现功能增购,此时即使没有升级版本但权限也得到了扩充;
为了解决上述问题,我们设计了权益包模型来定义权限集合,如下所示:
△权益包模型
权益包是可定制的业务属性,租户可以关联多个权益包,比如爱番番产品线存在租户版本权益包、400 电话功能权益包、用户坐席 license 权益包等分类。通过租户关联权益包来实现租户权限的最大集合。通过权益包模型可以很灵活、简单的解决上述提到的三个问题:
①、租户版本权益包解决版本差异:比如租户版本权益包可以分别定义不同版本的最大功能集合,用户购买不同版本即具备了权益包的所有权限;
②、功能权益包解决功能增购问题:将不同产品功能打包成单独的功能权益,用户通过购买功能实现权限的升级。
3.2.4 功能鉴权:GD-RBAC 模型 + 权益包模型以上我们介绍了 GD-RBAC 实现通过角色来关联不同的权限,通过权益包来关联最大权限集合,那么用户的最终权限等于两者权限交集,即如下所示:
△完整的功能鉴权
3.2.5 快速应用:客服代操作基于 GD-RBAC 和权益包模型,平台可以快速支持业务各种权限使用场景,这里举例客服代操作场景:
先说下代操作的概念:一组用户可以访问、操作另一组用户拥有的资源。典型例子:具有管辖权限的客服使用客服账号进入客户的爱番番系统,帮助客户完成系统配置。
客服 A 如果需要使用客服自己的账号进入张三的系统,通过设置组织结构树具备管辖权限并加入张三所在用户组,然后就可以实现客服的登录态且具备客户张三的操作权限。
△通过模型快速支持客服代操作业务
3.3 灵活扩展的数据鉴权除了以上我们介绍了功能鉴权之外,不同产品线有比较灵活的数据鉴权需求,比如不同部门、职位的账号如何实现数据权限的隔离或管辖?如何构建可定制的数据鉴权能力?我们定义了用户特征,分为通用特征和自定义特征:
通用的维度特征诸如维度、产品线、行业等每个 ToB 产品都具备的,我们通过维度来实现通用特征,比如每个部门、岗位都是不同的维度。业务方可以通过维度模型规范用户数据,支持特征级联,构建用户的特征网络,从而支持不同业务自定义不同的用户特征模型;
自定义特征是通过 k-v 形式存储的关键值对,用于灵活定义业务数据,比如客户来源、地址等非通用数据。
△数据权限抽象
△数据维度模型
我们把维度通过左右值树结构进行存储,使得维度具有一定层次关系,如果把数据绑定在维度节点上,资源间也有了层级关系,很适合解决一些具有层级关系的权限问题。比如业务方管理员可以定义组织结构、职位两个不同的维度,来分别建立组织结构树、职位树,如下图所示:
△左右值树在组织结构与职位的应用示例
那么为什么要采用左右值树存储维度呢?先介绍下左右值树基本原理:
使用左右序号对每一个节点进行标记,根节点的左值是树的最小序号,根节点的右值是树的最大序号,且高度是 0。
查询某节点的所有后代节点: 任意节点,其后代的左值一定比当前节点左值大,后代的右值一定比当前节点右值小
查询某个节点的祖先: 任意节点,其祖先的左值一定比当前节点左值小,其祖先的右值一定比当前节点右值大
计算节点下有多少子节点:包含自身:(右值 - 左值 + 1)/ 2;不含自身:(右值 - 左值 - 1)/ 2
节点排序:根据叶子结点排序。
下图是将维度 1-组织结构通过左右值树进行标记的图,其中 lv 表示树的层高,lleft 表示左边序号,lright 表示右边序号,通过左右值树进行存储标记,查找用户挂在哪个维度节点上,查询效率高。
△左右值树节点排序
最终我们可以通过两个维度节点的交集查询得到账号张三所在部门、职位的管辖结果集:
△维度管辖交集计算
所以通过左右值树的特点,对于查多写少的数据场景来说,可以快速的获取节点的所有子节点,进而高效的获取管辖关系判断数据权限。
3.4 高性能高可用的鉴权服务为了保障鉴权服务的高性能和高可用,平台部署在苏州、北京、广州三个机房异地容灾,并基于爱番番云原生基建,通过 istio envory 进行负载均衡和服务发现,并充分利用 istio 红利进行服务调用的超时熔断。在鉴权时通过多级缓存来提升鉴权速度和稳定性,具体来看是:
一级缓存:优先通过内存查询权限配置信息,其中考虑到内存信息时效性,内存信息有效期默认为 5 分钟;
二级缓存:如果内存失效或者查不到,则从 redis 中查询权限信息;
DB 兜底:如果 redis 查询为空可能代表有权限变更导致的 redis 失效,会从 DB 查询并刷新到 redis,通过超时配置进行 db 访问超时熔断。
△高性能高可用的鉴权服务
四、总结
本文主要介绍了百度 ToB 垂类账号权限平台的账号登录服务以及权限服务的设计:
通过配置化的事件编排灵活支持各产品线丰富的登录方式和登录策略,详细展开介绍了单点登录的设计与实践,并通过分阶段 session 控制与多种手段保障登录服务的安全可靠;
结合一个示例完整的介绍了通过 GD—RBAC 和权益包模型解决复杂业务的功能鉴权,并通过多维度鉴权灵活实现数据权限治理,助力业务和客户简单便捷的完成权限管控。
未来,平台将持续丰富账号和权限的能力,持续提升服务稳定性,秉持“技术为产品服务”的理念提供一站式账号与权限解决方案帮助 ToB 垂类各业务线更好的发挥业务产品价值。
五、作者介绍
本篇系百度 MEG ToB 垂类爱番番鲁班团队多位同学共同编写:
Corwin:资深研发工程师,先后负责多个业务方向,擅长平台和业务系统架构设计,用技术解决业务痛点;
Tao:高级研发工程师,负责业务基础、运营支撑等相关业务,擅长分布式与系统设计。
评论