

本文属于机器翻译版本。若本译文内容与英语原文存在差异，则一律以英文原文为准。

# 设计授权模型的最佳实践
<a name="design-authz-strategy"></a>

当您准备在软件应用程序中使用 Amazon Verified Permissions 服务时，立即编写策略语句可能会很困难。这就类似于在完全确定应用程序应该实现什么功能之前，通过编写 SQL 语句或 API 规范来开始开发应用程序的其他部分。相反，你应该从用户体验开始。然后，从用户经验开始倒推，找到一种实现方法。

在做这项工作时，您会发现自己存在几个疑问，比如：
+ 我的资源有哪些？ 它们是如何组织的？ 例如，文件是否位于一个文件夹中？
+ 资源的组织是否在权限模型中起作用？
+ 主体可以对每种资源执行哪些操作？
+ 主体如何获得这些权限？ 
+ 您是希望最终用户从 “管理员”、“操作员” 或 “ReadOnly” 等预定义权限中进行选择，还是应该创建临时策略声明？ 或者二者结合？
+ 角色是全球角色还是作用域角色？ 例如，“操作员” 是限制在单个租户内，还是 “操作员” 是指整个应用程序中的操作员？
+ 为了呈现用户体验，需要哪些类型的查询？ 例如，为了呈现用户的主页，是否需要列出主体可以访问的所有资源？ 
+ 用户有没有可能会不小心将自己排除在自己的资源之外？ 是否需要避免这种情况发生？

此练习的最终结果被称为**授权模型**；它定义了主体、资源、操作以及它们之间的相互关系。生成此模型不需要对 Cedar 或 Verified Permissions 服务有独特的了解。相反，它首先是一项用户体验设计练习，就像其他任何练习一样，可以体现在界面模型、逻辑图等工件中，以及对权限如何影响用户在产品中可以做的事情的总体描述。Cedar 的设计具有足够的灵活性，可以满足客户的模型需求，而不会为了符合 Cedar 的实现要求而强迫模型做出不自然的弯曲。因此，对期望的用户体验有清晰的了解是获得最佳模型的最佳方式。

要帮助回答问题并得出最优模型，请执行以下操作：
+ 在 [Cedar 政策语言参考指南中查看 Cedar 的设计模式](https://docs.cedarpolicy.com/overview/patterns.html)。
+ 考虑 Cedar 政策语言参考指南中的[最佳实践](https://docs.cedarpolicy.com/bestpractices/bp-naming-conventions.html)。
+ 考虑一下本页中包含的最佳实践。

**Topics**
+ [没有规范的“正确”模型](#design-no-canonical-correct-model)
+ [返回 403 个禁止的错误，而不是 404 未找到错误](#resource-existence-errors)
+ [将注意力集中在 API 操作之外的资源上](#design-focus-on-data-beyond-apis)
+ [多租户注意事项](#design-multi-tenancy-considerations)

## 没有规范的“正确”模型
<a name="design-no-canonical-correct-model"></a>

在设计授权模型时，没有一个唯一的正确答案。不同的应用程序可以对相似的概念有效地使用不同的授权模型，这是可以接受的。例如，思考一下计算机文件系统的表示形式。在类似 Unix 的操作系统中创建文件时，它不会自动继承父文件夹的权限。相比之下，在许多其他操作系统和大多数在线文件共享服务中，文件确实会继承其父文件夹的权限。这两个选项都有效，具体取决于应用程序进行优化的情况。

授权解决方案的正确性并非绝对，取决于它如何提供客户期望的体验，以及它是否能按照客户期望的方式保护他们的资源。如果您的授权模型能够做到这些，那么它就是一个成功的模型。

这就是为什么说，从所期望的用户体验开始设计是对创建有效授权模型最有帮助的先决条件。

## 返回 403 个禁止的错误，而不是 404 未找到错误
<a name="resource-existence-errors"></a>

对于包含与任何策略都不对应的实体（尤其是资源）的请求，最好返回 *403* Forbidden 错误，而不是 *404 未找到*错误。这提供了最高级别的安全性，因为您不会暴露实体是否存在，而只是该请求不符合策略存储中任何策略中的策略条件。

## 将注意力集中在 API 操作之外的资源上
<a name="design-focus-on-data-beyond-apis"></a>

在大多数应用程序中，权限都是围绕支持的资源建模的。例如，一个文件共享应用程序可能会将权限表示为可以对文件或文件夹执行的操作。这是一个良好的简单模型，它将底层实现和后端 API 操作抽象了出来。

相比之下，其他类型的应用程序，尤其是 Web 服务，通常会围绕 API 操作本身设计权限。例如，如果 Web 服务提供了一个名为 `createThing()` 的 API，则授权模型可能会定义相应的权限，或者在 Cedar 中定义名为 `createThing` 的 `action`。这适用于许多情况，并且使理解权限变得很简单。要调用 `createThing` 操作，您需要 `createThing` 操作权限。看起来很简单，对吧？

您会发现，已验证权限控制台中的[入门](getting-started-first-policy-store.md)流程包括直接通过 API 构建资源和操作的选项。这是一个有用的基准：您的策略存储库与其授权的 API 之间的直接映射。

但是，随着您进一步开发模型，这种以 API 为中心的方法可能不适合具有非常精细的授权模型的应用程序，因为这 APIs 只是客户真正想要保护的对象（底层数据和资源）的代名词。如果多人 APIs 控制对相同资源的访问权限，则管理员可能很难推断这些资源的路径并相应地管理访问权限。

例如，假设有一个包含组织成员的用户目录。可以将用户划分为组，其中一个安全目标是禁止未经授权的各方查看组成员资格。管理此用户目录的服务提供了两个 API 操作：
+ `listMembersOfGroup`
+ `listGroupMembershipsForUser`

客户可以使用其中任何一个操作来查看群组成员资格。因此，权限管理员必须记得协调对这*两个*操作的访问权限。如果您随后选择添加新的 API 操作来解决其他使用案例，情况会变得更加复杂，比如下面的情况：
+ `isUserInGroups`*（一个新 API，用于快速测试用户是否属于一个或多个组）*

从安全角度来看，此 API 为查看组成员资格开辟了第三条途径，这就破坏了管理员精心设计的权限。

我们建议您重点关注底层数据和资源及其关联操作。将这种方法应用于组成员资格示例将获得抽象权限，例如 `viewGroupMembership`，三个 API 操作都必须查询该权限。


| API 名称 | Permissions | 
| --- | --- | 
| listMembersOfGroup | 需要具有针对该组的 viewGroupMembership 权限 | 
| listGroupMembershipsForUser | 需要具有针对该用户的 viewGroupMembership 权限 | 
| isUserInGroups | 需要具有针对该用户的 viewGroupMembership 权限 | 

通过定义这一权限，管理员可以在现在和未来成功地控制查看组成员资格的权限。代价是，现在，每个 API 操作必须记录它可能需要的几种权限，并且管理员在制定权限时必须查阅此文档。如果是为满足安全要求，这可能是一个有效的折衷方案。

## 多租户注意事项
<a name="design-multi-tenancy-considerations"></a>

您可能需要开发供多个客户（使用您的应用程序的企业或*租户*）使用的应用程序，并将其与 Amazon Verified Permissions 集成。在开发授权模型之前，请制定多租户策略。您可以在*一个共享策略存储区中管理客户的策略*，也可以为*每个租户分配一个策略存储*。有关更多信息，请参阅*AWS 规范性*指南中的 [Amazon 已验证权限多租户设计注意事项](https://docs.aws.amazon.com/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/avp-design-considerations.html)。

1. 

**一个共享策略存储库**  
 所有租户共享一个保单存储库。应用程序将所有授权请求发送到共享策略存储区。

1. 

**每租户策略存储**  
 每个租户都有专门的保单存储。应用程序将查询不同的策略存储以获得授权决定，具体取决于提出请求的租户。

这两种策略都不会对您的 AWS 账单产生重大影响。那么，你应该如何设计自己的方法呢？ 以下是可能影响您的已验证权限多租户授权策略的常见条件。

**租户策略隔离**  
将每个租户的策略与其他租户的策略隔离开来对于保护租户数据非常重要。当每个租户都有自己的策略存储库时，他们每个人都有自己的一组独立的策略。

**授权流程**  
您可以在请求中使用策略存储库 ID 和每个租户的策略存储来识别发出授权请求的租户。对于共享策略存储，所有请求都使用相同的策略存储 ID。

**模板和架构管理**  
当您的应用程序有多个策略存储时，您的[策略模板](policy-templates.md)和[策略存储架构](schema.md)会在每个策略存储中增加一定程度的设计和维护开销。

**全球政策管理**  
您可能需要对每个租户应用一些*全局*策略。管理全局策略的开销水平因共享策略存储模式和按租户策略存储模式而异。

**租户离职**  
一些租户会为您的架构和策略提供特定于其案例的元素。当租户在您的组织中不再活跃并且您想要删除他们的数据时，工作量会因他们与其他租户的隔离程度而异。

**服务资源配额**  
已验证权限的资源和请求速率配额可能会影响您的多租户决策。有关限额的更多信息，请参阅[资源配额](quotas.md#quotas-resources)。

### 比较共享策略存储库和每租户策略存储库
<a name="design-multi-tenancy-considerations-chart"></a>

在共享和按租户策略商店模型中，每种考虑因素都需要自己的时间和资源投入水平。


| 
| 
| 考虑因素  | 共享策略存储库中的工作级别 | 每租户策略存储库中的工作量级别 | 
| --- |--- |--- |
| 租户策略隔离 | 中等。必须在策略和授权请求中包含租户标识符。 | 低。隔离是默认行为。其他租户无法访问特定于租户的政策。 | 
| 授权流程 | 低。所有查询都以一个策略存储为目标。 | 中等。必须维护每个租户与其策略存储 ID 之间的映射。 | 
| 模板和架构管理 | 低。必须使一个架构适用于所有租户。 | 高。架构和模板可能不那么复杂，但更改需要更多的协调和复杂性。 | 
| 全球政策管理 | 低。所有政策都是全球性的，可以集中更新。 | 高。您必须在入职时向每个策略存储区添加全局政策。在多个策略存储库之间复制全局策略更新。 | 
| 租户离职 | 高。必须仅识别和删除租户特定的政策。 | 低。删除策略存储。 | 
| 服务资源配额 | 高。租户共享影响策略存储的资源配额，例如架构大小、每个资源的策略大小以及每个策略存储的身份源。 | 低。每个租户都有专用资源配额。 | 

### 如何选择
<a name="design-multi-tenancy-considerations-how-to-choose"></a>

每个多租户应用程序都不一样。在做出架构决策之前，请仔细比较这两种方法及其注意事项。

如果您的应用程序不需要租户特定的策略并且使用单一[身份源](identity-sources.md#identity-sources.title)，那么为所有租户使用一个共享策略存储可能是最有效的解决方案。这样可以简化授权流程和全局策略管理。使用一个共享策略存储区退出租户所需的精力更少，因为应用程序不需要删除租户特定的策略。

 

但是，如果您的应用程序需要许多租户特定的策略，或者使用多个[身份源](identity-sources.md#identity-sources.title)，则每个租户的策略存储可能是最有效的。您可以使用向每个租户授予每个策略存储的权限的 IAM 策略来控制对租户策略的访问权限。退出租户涉及删除其策略存储；在 shared-policy-store环境中，您必须查找并删除租户特定的策略。