本文是 2.3 探索横切关注点 - 使用授权和权限系统* 一节的扩充内容,重点探讨了授权在分布式和微服务系统中遇到的挑战,以及 ABP Framework 中采用的解决方案。
认证 & 授权
- 认证(Authentication):确认用户身份
- 授权(Authorization):授予用户访问资源的权限
关于认证和授权的基础内容,可回顾:2.3 探索横切关注点 - 使用授权和权限系统 。
在微服务和分布式系统,对于权限,我们需要考虑的更多,比如:如何在一个微服务中设置权限,而在另一个微服务中检测权限,以及权限如何集中管理。
分布式系统中,认证和授权信息通常以 令牌(Token) 方式描述,所以先了解令牌内容的格式以及令牌如何存储。
访问令牌
访问令牌内容如下:
访问令牌内容由多个声明(Claim)组成,包含标准声明和自定义声明。
标准声明(claims)
Sub
: 用户IdRole
: 用户角色Scope
: 允许的范围
存储访问令牌
对于WEB应用程序,存储在浏览器本地存储(Local Storage),键为 access_token
id_token
。
对于其他类型的应用程序,同样可以在获取到访问令牌之后,在本地存储。
授权类型 & 需求
- 基于声明的策略
- 开/关风格权限(通常是基于用户和角色)
- 开关数据保存在权限数据库中
- 自定义策略
- 检查权限数据库并直接应用自定义逻辑
- 基于资源的策略
- 检查权限数据库并根据请求的资源应用自定义逻辑
基于资源的策略
基于资源的策略,用一句话来描述就是:用户或客户端(C)是否可以在资源(R)上执行操作(A) ?
举个例子:权限控制:当前用户是否可以编辑产品信息?
我们使用基于资源的策略,来实现以上权限控制:操作(A)对应编辑;资源(R)对应产品信息;用户(C)对应当前用户。
关于编辑可能存在的授权逻辑:
- 如果产品被锁定,不允许
- 如果产品是由当前用户创建的,则允许
- 如果用户有编辑产品的权限,则允许,该权限由管理员管理用户
- 如果用户所属的角色有编辑权限,则允许
- 只有当用户在产品所属的部门中工作时,才允许
基于资源的策略,可以很好地处理以上授权逻辑。
需求:授权组件
设计一个良好的授权系统,我们通常需要考虑的四个点:
- 策略实施点
- 策略决策点
- 策略信息点
- 策略管理点
策略实施点(PEP: Policy Enforcement Point),即在哪里实施授权? 在API网关中?还是在当前服务中?
策略决策点(PDP: Policy Decision Point)即在哪里执行授权逻辑?。在当前的过程中?还是在专门的服务中?
策略信息点(PIP: Policy Information Point)即从哪里以及如何获取执行授权逻辑的数据? 直接访问数据库?还是按需收集?
策略管理点(PAP: Policy Administration Point)即直接访问数据库?按需收集? 通常系统中提供统一的管理界面。
架构讨论
基于前面授权组件的四个点,我们需要做一些讨论,以及清楚其实现方式的利弊。
讨论1:外部授权服务
策略实施点(PEP)发生在微服务中,在微服务中调用授权服务(PDP),进行权限控制。
如何获取应用数据?
- 直接从服务数据库中读取?
- 在授权服务调用发送数据?
- 将数据预复制到授权服务的数据库中?
其他问题:
- 授权服务调用上的网络延迟。
- 授权服务成为瓶颈。
讨论2:在API网关检查
策略实施点(PEP)发生在网关中,在网关中调用授权服务(PDP),进行权限控制。
优点
- 将授权逻辑与微服务解耦
- 防止未经授权的请求调用微服务
缺点
- 需要在API中自定义处理逻辑,受限于网关实现选择的技术
- 需要将权限映射到HTTP,如:URL, HTTP 方法和 HTTP头。
讨论3:授权类库
将策略实施点、策略决策点、策略信息点的操作封装为通用的类库,在微服务中调用管理权限。
优点
- 将授权逻辑与微服务逻辑解耦
- 应用程序数据可用
- 可以自定义授权逻辑
缺点
- 如果使用不同技术开发的微服务,如:.NET 、Python、Java,则存在问题,不能跨开发语言。
综合以上三种方案的优缺点,采用授权类库的方式。
讨论4:如何保存权限数据?
方式一:每个微服务在自己的数据库中都有自己的权限表?
- 难以管理(加载和保存)权限
- 很难得到所有的权限
- 服务的责任太大
方式二:一个中央权限数据库?
- 所有服务都通过授权库直接连接到该数据库。
综合以上两种方式,采用中央权限数据库,实现策略信息点。
讨论5:如何检查和管理所有权限?
方式一:询问所有微服务的权限需求?
问题:服务发现、性能、服务上的负载太多,难以管理权限。
方式二:计算微服务中的所有权限并在中心位置预缓存?
管理和刷新缓存非常困难,任意的数据更改可能会影响许多用户的权限,缓存会太大。
方式三:在权限管理服务的中心位置计算权限?
非常适合开/关风格的权限,对微服务内部的依赖最小。
综合以上三种方式,采用在权限管理服务的中心位置计算权限,实现策略管理点(PAP)。
ABP 解决方案
基于以上的架构分析,我们来看一看在 ABP Framework 中的具体实现。
权限管理
定义权限
检测权限
授予权限
定义权限
定义权限相关参数:
- 权限名称
- 显示本地化权限名称
- 其他依赖:功能,全局功能
- 自定义数据
微服务中的权限管理
权限管理UI
管理UI数据来自,权限管理微服务;其他微服务中权限数据(权限定义、权限组、授权信息)保存到权限管理数据库中。
在权限管理微服务中和其他微服务一样使用 ABP 授权类库。
基于资源的授权
ABP 采用基于资源的授权策略
权限过滤
需要获取 资源(实体) 列表,根据业务规则、用户预设和权限进行筛选。
上图代码中,根据实体中的信息实施相应的业务规则,设置权限:
OrganizationId
组织Id,允许组织CategoryId
类别Id,允许类别AssignedUserId
分配用户Id,允许分配用户可见CreatorUserId
创建用户Id,允许创建用户可见IsResolved
是否解决,在UI过滤
更新权限定义
关于服务启动
- 计算所有权限定义的哈希值
- 与分布式缓存中的哈希值进行比较
- 序列化和保存权限定义(仅更改/新增)
- 更新权限戳以通知其他服务
- 使用微服务名称作为缓存键前缀
获取权限定义
获取逻辑
- 如果是最新的,检查权限缓存戳并使用内存中的缓存。频率控制(30秒)。
- 如果不同,获取并反序列化权限定义。
在服务器启动时,预缓存权限定义。
用户界面:检测所有权限
获取当前用户的所有授予权限的列表!我们为什么需要它?用于控制显示或隐藏菜单项;用于控制显示或隐藏UI中的部分内容,如:按钮,标题,工具栏等;用于检查SPA的客户端权限。
管理:管理所有权限
将管理一个角色或用户的所有权限在同一个地方显示。
结论
微服务和分布式系统中的授权系统设计:
- 设计注意事项:策略实施点、策略决策点、策略信息点和策略管理点。
- 集中存储权限数据。
- 集中管理,采用开关风格权限。
- 总是要求微服务自定义和基于资源的权限。
- 依赖数据库查询进行基于权限的过滤。