微服务和分布式系统中的授权解决方案
创建时间:2022年11月23日 11:45:27 最后更新:2024年06月16日 13:19:34

本文是 2.3 探索横切关注点 - 使用授权和权限系统* 一节的扩充内容,重点探讨了授权在分布式和微服务系统中遇到的挑战,以及 ABP Framework 中采用的解决方案。

认证 & 授权

  • 认证(Authentication):确认用户身份
  • 授权(Authorization):授予用户访问资源的权限

关于认证和授权的基础内容,可回顾:2.3 探索横切关注点 - 使用授权和权限系统

在微服务和分布式系统,对于权限,我们需要考虑的更多,比如:如何在一个微服务中设置权限,而在另一个微服务中检测权限,以及权限如何集中管理。

分布式系统中,认证和授权信息通常以 令牌(Token) 方式描述,所以先了解令牌内容的格式以及令牌如何存储

访问令牌

访问令牌内容如下:

access_token_content

访问令牌内容由多个声明(Claim)组成,包含标准声明自定义声明

标准声明(claims)

  • Sub: 用户Id
  • Role: 用户角色
  • Scope: 允许的范围

存储访问令牌

对于WEB应用程序,存储在浏览器本地存储(Local Storage),键为 access_token id_token

token_store

对于其他类型的应用程序,同样可以在获取到访问令牌之后,在本地存储。

授权类型 & 需求

  • 基于声明的策略
  • 开/关风格权限(通常是基于用户角色
    • 开关数据保存在权限数据库
  • 自定义策略
    • 检查权限数据库并直接应用自定义逻辑
  • 基于资源的策略
    • 检查权限数据库并根据请求的资源应用自定义逻辑

基于资源的策略

基于资源的策略,用一句话来描述就是:用户客户端(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),进行权限控制。

external_authorization_service

如何获取应用数据?

  • 直接从服务数据库中读取?
  • 在授权服务调用发送数据?
  • 将数据预复制到授权服务的数据库中?

其他问题:

  • 授权服务调用上的网络延迟。
  • 授权服务成为瓶颈。

讨论2:在API网关检查

策略实施点(PEP)发生在网关中,在网关中调用授权服务(PDP),进行权限控制。

check_api_gateway

优点

  • 将授权逻辑与微服务解耦
  • 防止未经授权的请求调用微服务

缺点

  • 需要在API中自定义处理逻辑,受限于网关实现选择的技术
  • 需要将权限映射到HTTP,如:URL, HTTP 方法和 HTTP头。

讨论3:授权类库

将策略实施点、策略决策点、策略信息点的操作封装为通用的类库,在微服务中调用管理权限。

authorization_library

优点

  • 将授权逻辑与微服务逻辑解耦
  • 应用程序数据可用
  • 可以自定义授权逻辑

缺点

  • 如果使用不同技术开发的微服务,如:.NET 、Python、Java,则存在问题,不能跨开发语言。

综合以上三种方案的优缺点,采用授权类库的方式

讨论4:如何保存权限数据?

方式一:每个微服务在自己的数据库中都有自己的权限表?

  • 难以管理(加载和保存)权限
  • 很难得到所有的权限
  • 服务的责任太大

方式二:一个中央权限数据库?

  • 所有服务都通过授权库直接连接到该数据库。

综合以上两种方式,采用中央权限数据库,实现策略信息点。

讨论5:如何检查和管理所有权限?

方式一:询问所有微服务的权限需求?

问题:服务发现、性能、服务上的负载太多,难以管理权限。

方式二:计算微服务中的所有权限并在中心位置预缓存?

管理和刷新缓存非常困难,任意的数据更改可能会影响许多用户的权限,缓存会太大。

方式三:在权限管理服务的中心位置计算权限?

非常适合开/关风格的权限,对微服务内部的依赖最小。

综合以上三种方式,采用在权限管理服务的中心位置计算权限,实现策略管理点(PAP)。

ABP 解决方案

基于以上的架构分析,我们来看一看在 ABP Framework 中的具体实现。

权限管理

定义权限

define_permissions

检测权限

check_permissions

授予权限

grant_permissions

定义权限

apb_permission_define

定义权限相关参数:

  • 权限名称
  • 显示本地化权限名称
  • 其他依赖:功能,全局功能
  • 自定义数据

微服务中的权限管理

权限管理UI

permission_manage_ui

管理UI数据来自,权限管理微服务;其他微服务中权限数据(权限定义、权限组、授权信息)保存到权限管理数据库中。

abp_permission_microservice

在权限管理微服务中和其他微服务一样使用 ABP 授权类库。

基于资源的授权

ABP 采用基于资源的授权策略

resource_base_permission

权限过滤

需要获取 资源(实体) 列表,根据业务规则用户预设权限进行筛选。

aggregateroot_question

上图代码中,根据实体中的信息实施相应的业务规则,设置权限:

  • OrganizationId 组织Id,允许组织
  • CategoryId 类别Id,允许类别
  • AssignedUserId 分配用户Id,允许分配用户可见
  • CreatorUserId 创建用户Id,允许创建用户可见
  • IsResolved 是否解决,在UI过滤

更新权限定义

关于服务启动

  • 计算所有权限定义的哈希值
  • 与分布式缓存中的哈希值进行比较
  • 序列化和保存权限定义(仅更改/新增)
  • 更新权限戳以通知其他服务
  • 使用微服务名称作为缓存键前缀

update_permission_define

获取权限定义

获取逻辑

  • 如果是最新的,检查权限缓存戳并使用内存中的缓存。频率控制(30秒)。
  • 如果不同,获取并反序列化权限定义。

在服务器启动时,预缓存权限定义。

abp_get_permission_define

用户界面:检测所有权限

获取当前用户所有授予权限的列表!我们为什么需要它?用于控制显示或隐藏菜单项;用于控制显示或隐藏UI中的部分内容,如:按钮,标题,工具栏等;用于检查SPA的客户端权限。

check_all_permission

管理:管理所有权限

将管理一个角色或用户的所有权限在同一个地方显示。

manage_all_permission

结论

微服务和分布式系统中的授权系统设计:

  • 设计注意事项:策略实施点、策略决策点、策略信息点和策略管理点。
  • 集中存储权限数据。
  • 集中管理,采用开关风格权限。
  • 总是要求微服务自定义和基于资源的权限。
  • 依赖数据库查询进行基于权限的过滤。