

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

# 使用 Amazon 验证权限实施 PDP
<a name="avp"></a>

Amazon Verified Permissions 是一项可扩展、精细的权限管理和授权服务，可用于实施政策决策点 (PDP)。作为策略引擎，它可以帮助您的应用程序实时验证用户操作，并突出显示权限过高或无效的权限。它通过外部化授权和集中策略管理和管理，帮助您的开发人员更快地构建更安全的应用程序。通过将授权逻辑与应用程序逻辑分开，Verified Permissions 支持策略解钩。

通过使用已验证的权限来实施 PDP 并在应用程序中实现最低权限和持续验证，开发人员可以使其应用程序访问权限与 [Zero Trust](https://aws.amazon.com/security/zero-trust/) 原则保持一致。此外，安全和审计团队可以更好地分析和审计谁有权访问应用程序中的哪些资源。Verified Permissions 使用 C [ed](https://www.cedarpolicy.com/en) ar（一种专门构建且安全至上的开源策略语言）根据基于角色的访问控制 (RBAC) 和基于属性的访问控制 (ABAC) 来定义基于策略的访问控制，以实现更精细的上下文感知访问控制。

已验证权限为 SaaS 应用程序提供了一些有用的功能，例如能够使用多个身份提供商（例如 Amazon Cognito、Google 和 Facebook）启用多租户授权。另一项对 SaaS 应用程序特别有用的已验证权限功能是支持基于每个租户的自定义角色。如果您正在设计客户关系管理 (CRM) 系统，则一个租户可能会根据一组特定的标准来定义销售机会访问的粒度。另一个租户可能有其他定义。已验证权限中的底层权限系统可以支持这些变体，这使其成为 SaaS 用例的绝佳选择。Verified Permissions 还支持编写适用于所有租户的策略，因此，作为 SaaS 提供商，可以直接应用护栏策略来防止未经授权的访问。

![\[使用 Amazon 已验证的权限实施 PDP\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp.png)


**为什么要使用已验证的权限？**

使用身份提供商（例如 [Amazon Cognito](https://aws.amazon.com/cognito/)）的已验证权限，为您的应用程序提供更加动态的、基于策略的访问管理解决方案。您可以构建应用程序来帮助用户共享信息和进行协作，同时维护其数据的安全性、机密性和隐私。Verified Permissions 为您提供精细的授权系统，可根据您的身份和资源的角色和属性强制执行访问权限，从而帮助您降低运营成本。您可以定义策略模型，在中心位置创建和存储策略，并在几毫秒内评估访问请求。

在已验证的权限中，您可以使用一种名为 Cedar 的简单、人类可读的声明性语言来表达权限。使用 Cedar 编写的策略可以在团队之间共享，无论每个团队的应用程序使用何种编程语言。

**使用已验证权限时应考虑的事项**

在已验证的权限中，您可以创建策略并将其作为配置的一部分自动执行。您还可以在运行时创建策略作为应用程序逻辑的一部分。作为最佳实践，在租户入职和置备过程中创建策略时，应使用持续集成和持续部署 (CI/CD) 管道来管理、修改和跟踪策略版本。或者，应用程序可以管理、修改和跟踪策略版本；但是，应用程序逻辑本身并不执行此功能。要在应用程序中支持这些功能，必须明确设计应用程序以实现此功能。

如果需要提供来自其他来源的外部数据才能做出授权决定，则必须检索这些数据并将其作为授权请求的一部分提供给 Verified Permissions。默认情况下，此服务不会检索其他上下文、实体和属性。

# 雪松概述
<a name="cedar"></a>

Cedar 是一种灵活、可扩展且可扩展的基于策略的访问控制语言，可帮助开发人员将应用程序权限表示为策略。管理员和开发人员可以定义允许或禁止用户对应用程序资源进行操作的策略。可以将多个策略附加到单个资源。当您的应用程序的用户尝试对资源执行操作时，您的应用程序会向 Cedar 策略引擎请求授权。Cedar 会评估适用的政策并返回`ALLOW`或`DENY`决定。Cedar 支持任何类型的委托人和资源的授权规则，允许基于角色的访问控制 (RBAC) 和基于属性的访问控制 (ABAC)，并支持通过自动推理工具进行分析。

Cedar 允许您将业务逻辑与授权逻辑分开。当您使用应用程序的代码发出请求时，您可以调用 Cedar 的授权引擎来确定该请求是否获得授权。如果获得授权（决定是`ALLOW`），则您的应用程序可以执行请求的操作。如果未获得授权（决定是`DENY`），则您的应用程序可能会返回错误消息。Cedar 的主要特点包括：
+ **表现力** — Cedar 专为支持授权用例而设计，并且在开发时考虑了人类的可读性。
+ **性能** — Cedar 支持索引策略以实现快速检索，并提供具有有限延迟的快速且可扩展的实时评估。
+ **分析** — Cedar 支持分析工具，这些工具可以优化您的策略并验证您的安全模型。

欲了解更多信息，请访问 [Cedar 网站](https://www.cedarpolicy.com/)。

# 示例 1：具有经过验证的权限和 Cedar 的基本 ABAC
<a name="avp-basic-abac-examples"></a>

在此示例场景中，Amazon 验证权限用于确定允许哪些用户访问虚构的 Payroll 微服务中的信息。本节包含 Cedar 代码片段，用于演示如何使用 Cedar 来做出访问控制决策。这些示例并不是为了全面探索 Cedar 和已验证权限提供的功能。有关 Cedar 的更全面概述，请参阅 [Cedar 文档](https://docs.cedarpolicy.com/)。

在下图中，我们想强制执行与该`viewSalary``GET`方法相关的两条一般业务规则：*员工可以查看自己的工资*，*员工可以查看向他们汇报的任何人的工资。*您可以使用已验证的权限策略来强制执行这些业务规则。

![\[使用 Amazon 验证权限和 Cedar 实现 PDP 的基本 ABAC 实施示例\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-1.png)


*员工可以查看自己的工资。*

在 Cedar 中，基本结构是一个*实体*，它代表委托人、行动或资源。要提出授权请求并使用已验证权限策略开始评估，您需要提供*委托人、**操作**、资源*和*实体列表。*
+ 委托人 (`principal`) 是登录的用户或角色。
+ 操作 (`action`) 是请求所评估的操作。
+ 资源 (`resource`) 是操作正在访问的组件。
+ 实体列表 (`entityList`) 包含评估请求所需的所有必需实体。

为了满足业务规则*员工可以查看自己的工资*，您可以提供如下所示的已验证权限策略。

```
permit (
    principal,
    action == Action::"viewSalary",
    resource
)
when {
    principal == resource.owner
};
```

此策略的评估结果`Action`是`viewSalary`，请求中的资源`ALLOW`是否具有等于委托人的属性所有者。例如，如果 Bob 是请求薪金报告的登录用户，同时也是薪金报告的所有者，则策略的评估结果为。`ALLOW`

以下授权请求已提交给已验证的权限，以供示例策略进行评估。在此示例中，Bob 是`viewSalary`发出请求的登录用户。因此，Bob 是实体类型的主体`Employee`。Bob 正在尝试执行的操作是`viewSalary,`，`viewSalary`将显示的资源`Salary-Bob`与类型相同`Salary`。为了评估 Bob 是否可以查看`Salary-Bob`资源，您需要提供一个实体结构，该结构将值为的类型 `Employee``Bob`（委托人）链接到具有该类型的资源的所有者属性`Salary`。您可以在中提供此结构`entityList`，其中与之关联的属性`Salary`包括所有者，所有者指定`entityIdentifier`包含类型`Employee`和值的`Bob`。Verified Permissions 将授权请求中`principal`提供的权限与与`Salary`资源关联的`owner`属性进行比较，以做出决策。

```
{
  "policyStoreId": "PAYROLLAPP_POLICYSTOREID",
  "principal": {
    "entityType": "PayrollApp::Employee",
    "entityId": "Bob"
  },
  "action": {
    "actionType": "PayrollApp::Action",
    "actionId": "viewSalary"
  },
  "resource": {
    "entityType": "PayrollApp::Salary",
    "entityId": "Salary-Bob"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
          "entityType": "PayrollApp::Salary",
          "entityId": "Salary-Bob"
        },
        "attributes": {
          "owner": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Bob"
            }
          }
        }
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Bob"
        },
        "attributes": {}
      }
    ]
  }
}
```

对已验证权限的授权请求将以下内容作为输出返回，其中属性`decision`为`ALLOW`或`DENY`。

```
{
    "determiningPolicies": 
        [ 
            {
                "determiningPolicyId": "PAYROLLAPP_POLICYSTOREID" 
            }
        ],
    "decision": "ALLOW",
    "errors": [] 
}
```

在本例中，由于 Bob 正在尝试查看自己的工资，因此发送给 “已验证权限” 的授权请求计算为`ALLOW`。但是，我们的目标是使用经过验证的权限来强制执行两项业务规则。规定以下内容的业务规则也应为真：

*员工可以查看向他们汇报的任何人的工资。*

为了满足此业务规则，您可以提供其他策略。以下策略评估操作`ALLOW`是否为`viewSalary`，请求中的资源是否具有等`owner.manager`于委托人的属性。例如，如果 Alice 是请求薪资报告的登录用户，而 Alice 是报告所有者的经理，则策略的评估结果为。`ALLOW`

```
permit (
    principal,
    action == Action::"viewSalary",
    resource
)
when {
    principal == resource.owner.manager
};
```

以下授权请求已提交给已验证的权限，以供示例策略进行评估。在此示例中，Alice 是`viewSalary`发出请求的登录用户。因此，Alice 是委托人，而实体属于这种类型`Employee`。Alice 尝试执行的操作是`viewSalary`，`viewSalary`将显示的资源属于值为`Salary`的类型`Salary-Bob`。为了评估 Alice 能否查看`Salary-Bob`资源，您需要提供一个实体结构，该结构将值为的类型`Employee`链接`Alice`到该`manager`属性，然后该`owner`属性必须与值为的类型的`Salary`属性相关联`Salary-Bob`。您可以在中提供此结构`entityList`，其中与之关联的属性`Salary`包括所有者，所有者指定`entityIdentifier`包含类型`Employee`和值的`Bob`。“已验证权限” 首先检查`owner`属性，该属性将根据类型`Employee`和值`Bob`进行计算。然后，Verified Permissions 会评估`Employee`与之关联的`manager`属性，并将其与提供的委托人进行比较以做出授权决定。在这种情况下，之所以做出决定，`ALLOW`是因为`principal `和`resource.owner.manager`属性是等效的。

```
{
  "policyStoreId": "PAYROLLAPP_POLICYSTOREID",
  "principal": {
    "entityType": "PayrollApp::Employee",
    "entityId": "Alice"
  },
  "action": {
    "actionType": "PayrollApp::Action",
    "actionId": "viewSalary"
  },
  "resource": {
    "entityType": "PayrollApp::Salary",
    "entityId": "Salary-Bob"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Alice"
        },
        "attributes": {
          "manager": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "None"
            }
          }
        },
        "parents": []
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Salary",
          "entityId": "Salary-Bob"
        },
        "attributes": {
          "owner": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Bob"
            }
          }
        },
        "parents": []
      },
      {
        "identifier": {
          "entityType": "PayrollApp::Employee",
          "entityId": "Bob"
        },
        "attributes": {
          "manager": {
            "entityIdentifier": {
              "entityType": "PayrollApp::Employee",
              "entityId": "Alice"
            }
          }
        },
       "parents": []
      }
    ]
  }
}
```

到目前为止，在本示例中，我们提供了与该`viewSalary`方法相关的两个业务规则，*员工可以查看自己的工资*，*员工可以查看向他们汇报的任何人的工资*，将已验证权限作为策略来独立满足每条业务规则的条件。您也可以使用单个 “已验证权限” 策略来满足两个业务规则的条件：

*员工可以查看自己的工资和向他们汇报的任何人的工资。*

当您使用之前的授权请求时，以下策略会评估为操作是`viewSalary`，请求中的资源`ALLOW`是否具有等于的属性 `owner.manager``principal`，还是等于的`principal`属性`owner`。

```
permit (
    principal,
    action == PayrollApp::Action::"viewSalary",
    resource
)
when {
    principal == resource.owner.manager ||
    principal == resource.owner
};
```

例如，如果 Alice 是请求薪资报告的登录用户，如果 Alice 是报告所有者的经理或所有者，则策略的评估结果为。`ALLOW`

有关在 Cedar 策略中使用逻辑运算符的更多信息，请参阅 [Cedar 文档](https://docs.cedarpolicy.com/policies/syntax-operators.html)。

# 示例 2：具有经过验证的权限和 Cedar 的基本 RBAC
<a name="avp-basic-rbac-examples"></a>

此示例使用已验证的权限和 Cedar 来演示基本的 RBAC。如前所述，Cedar的基本结构是一个实体。开发人员定义自己的实体，并且可以选择在实体之间创建关系。以下示例包括三种类型的实体：`Users``Roles`、和`Problems`。 `Students`并且`Teachers`可以被视为该类型的实体`Role,`，每个实体`User`都可以与零或任何一个相关联`Roles`。

![\[使用 Amazon 验证权限和 Cedar 实施PDP的基本 RBAC 示例\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-2.png)


在 Cedar 中，这些关系是通过将链接`Role``Student`到`User``Bob`作为其父项来表达的。这种关联在逻辑上将所有学生用户分组到一个组中。有关在 Cedar 中进行分组的更多信息，请参阅 [Cedar 文档](https://docs.cedarpolicy.com/overview/terminology.html#term-group)。

以下策略评估与该类型的逻辑组相关联的所有委托`Students`人的操作`submitProblem,`决策`ALLOW`。`Role`

```
permit (
    principal in ElearningApp::Role::"Students",
    action == ElearningApp::Action::"submitProblem",
    resource
);
```

以下策略根据操作的决策进行评估`answerProblem`，`submitProblem`或者`ALLOW`针对与该类型的逻辑组相关联的所有委托`Teachers`人进行评估。`Role`

```
permit (
    principal in ElearningApp::Role::"Teachers",
    action in [
        ElearningApp::Action::"submitProblem",
        ElearningApp::Action::"answerProblem"
    ],
    resource
);
```

为了评估使用这些策略的请求，评估引擎需要知道授权请求中引用的委托人是否为相应组的成员。因此，应用程序必须将相关的群组成员资格信息作为授权请求的一部分传递给评估引擎。这是通过属性完成的，它使您能够向 Cedar 评估引擎提供授权调用中涉及的委托人和资源的属性和组成员身份数据。`entities`在以下代码中，组成员资格通过定义`User::"Bob"`为调用父代来表示`Role::"Students"`。

```
{
  "policyStoreId": "ELEARNING_POLICYSTOREID",
  "principal": {
    "entityType": "ElearningApp::User",
    "entityId": "Bob"
  },
  "action": {
    "actionType": "ElearningApp::Action",
    "actionId": "answerProblem"
  },
  "resource": {
    "entityType": "ElearningApp::Problem",
    "entityId": "SomeProblem"
  },
  "entities": {
    "entityList": [
        {
            "identifier": {
                "entityType": "ElearningApp::User",
                "entityId": "Bob"
            },
            "attributes": {},
            "parents": [
                {
                    "entityType": "ElearningApp::Role",
                    "entityId": "Students"
                } 
            ]
        },
        {
          "identifier": {
            "entityType": "ElearningApp::Problem",
            "entityId": "SomeProblem"
          },
          "attributes": {},
          "parents": []
        }
      ]
  }
}
```

在此示例中，Bob 是`answerProblem`发出请求的登录用户。因此，Bob 是委托人，而实体属于该类型`User`。Bob 想要执行的操作是`answerProblem`。为了评估 Bob 能否执行`answerProblem`操作，您需要提供一个实体结构，该结构将该实体`User`与值关联起来，`Bob`并通过将父实体列为`Role::"Students"`，来分配其组成员资格。由于用户组`Role::"Students"`中的实体只能执行操作，因此此授权请求的计算结果为。`submitProblem` `DENY`

另一方面，如果值为`Alice`且属于该组的类型`User``Role::"Teachers"`尝试执行操作，则授权请求的计算结果为`ALLOW`，因为该策略规定允许组中的委托人对`Role::"Teachers"`所有资源执行操作。`answerProblem` `answerProblem`以下代码显示了评估结果为的此类授权请求。`ALLOW`

```
{
  "policyStoreId": "ELEARNING_POLICYSTOREID",
  "principal": {
    "entityType": "ElearningApp::User",
    "entityId": "Alice"
  },
  "action": {
    "actionType": "ElearningApp::Action",
    "actionId": "answerProblem"
  },
  "resource": {
    "entityType": "ElearningApp::Problem",
    "entityId": "SomeProblem"
  },
  "entities": {
    "entityList": [
        {
            "identifier": {
                "entityType": "ElearningApp::User",
                "entityId": "Alice"
            },
            "attributes": {},
            "parents": [
                {
                    "entityType": "ElearningApp::Role",
                    "entityId": "Teachers"
                } 
            ]
        },
        {
            "identifier": {
                "entityType": "ElearningApp::Problem",
                "entityId": "SomeProblem"
            },
            "attributes": {},
            "parents": []
        }
      ]
  }
}
```

# 示例 3：使用 RBAC 进行多租户访问控制
<a name="avp-mt-abac-examples"></a>

要详细说明前面的 RBAC 示例，您可以扩展您的要求以包括 SaaS 多租户，这是 SaaS 提供商的常见要求。在多租户解决方案中，总是代表给定租户提供资源访问权限。也就是说，租户 A 的用户无法查看租户 B 的数据，即使这些数据在逻辑上或物理上并置在系统中。以下示例说明了如何使用多个[已验证权限策略存储](https://docs.aws.amazon.com/verifiedpermissions/latest/userguide/policy-stores.html)来实现租户隔离，以及如何使用用户角色在租户内定义权限。

使用每租户策略存储设计模式是在使用已验证权限实施访问控制的同时保持租户隔离的最佳实践。在这种情况下，租户 A 和租户 B 的用户请求将分别根据不同的策略存储`DATAMICROSERVICE_POLICYSTORE_A`和进行`DATAMICROSERVICE_POLICYSTORE_B`验证。有关多租户 SaaS 应用程序的已验证权限设计注意事项的更多信息，请参阅[已验证权限多租户设计注意事项部分](avp-design-considerations.md)。

![\[使用 RBAC、Amazon 验证权限和 Cedar 进行多租户访问控制的示例\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-3.png)


以下策略位于`DATAMICROSERVICE_POLICYSTORE_A`策略存储中。它验证主体是否将成为该类型`Role`组`allAccessRole`的一部分。在这种情况下，将允许委托人对与租户 A 关联的所有资源执行`viewData`和`updateData`操作。

```
permit (
    principal in MultitenantApp::Role::"allAccessRole",
    action in [
        MultitenantApp::Action::"viewData",
        MultitenantApp::Action::"updateData"
    ],
    resource
);
```

以下策略位于`DATAMICROSERVICE_POLICYSTORE_B`策略存储中。第一个策略验证委托人是否属于该类型`updateDataRole``Role`组。假设是这样，则它允许委托人对与租户 B 关联的资源执行操作。`updateData`

```
permit (
    principal in MultitenantApp::Role::"updateDataRole",
    action == MultitenantApp::Action::"updateData",
    resource
);
```

第二项政策规定，`Role`应允许属于该类型`viewDataRole`组的委托人对与租户 `viewData` B 关联的资源执行操作。

```
permit (
    principal in MultitenantApp::Role::"viewDataRole",
    action == MultitenantApp::Action::"viewData",
    resource
);
```

租户 A 发出的授权请求需要发送到`DATAMICROSERVICE_POLICYSTORE_A`策略存储区，并由属于该存储的策略进行验证。在本例中，前面作为本示例一部分讨论的第一个策略对其进行了验证。在此授权请求中，类型`User`为的委托人`Alice`正在请求执行`viewData`操作。主体属于`allAccessRole`类型组`Role`。Alice 正在尝试对`SampleData`资源执行`viewData`操作。由于 Alice 有这个`allAccessRole`角色，所以这个评估会产生一个`ALLOW`决策。

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_A",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "viewData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

相反，如果您查看租户 B 发出的请求`User Bob`，则会看到类似以下授权请求的内容。该请求之所以被发送到`DATAMICROSERVICE_POLICYSTORE_B`策略存储，是因为它来自租户 B。在此请求中，委托`Bob`人想要`updateData`对资源`SampleData`执行操作。但是，`Bob`不属于有权访问该资源操作`updateData`的群组。因此，该请求会导致决`DENY`策。

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_B",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Bob"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "updateData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Bob"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "viewDataRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

在第三个示例中，`User Alice `尝试对资源执行`viewData`操作`SampleData`。此请求被定向到`DATAMICROSERVICE_POLICYSTORE_A`策略存储，因为委托人`Alice`属于租户 A`Alice`，属于该`allAccessRole`类型的组`Role`，允许她对资源执行操作。`viewData`因此，请求会导致`ALLOW`决策。

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE_A",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "viewData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
      },
      {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": []
      }
    ]
  }
}
```

# 示例 4：使用 RBAC 和 ABAC 进行多租户访问控制
<a name="avp-mt-abac-rbac-examples"></a>

为了增强上一节中的 RBAC 示例，您可以向用户添加属性，以创建用于多租户访问控制的 RBAC-ABAC 混合方法。此示例包含与上一个示例相同的角色，但添加了用户属`account_lockout_flag`性和上下文参数`uses_mfa`。该示例还采用了不同的方法来实现多租户访问控制，即同时使用 RBAC 和 ABAC，并为每个租户使用一个共享策略存储而不是不同的策略存储。

![\[使用 RBAC、ABAC、Amazon 验证权限和 Cedar 进行多租户访问控制的示例\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-4.png)


此示例表示一种多租户 SaaS 解决方案，在该解决方案中，您需要为租户 A 和租户 B 提供授权决策，与前面的示例类似。

为了实现用户锁定功能，该示例在授权请求中`account_lockout_flag`向`User`实体委托人添加了该属性。此标志锁定用户对系统的访问权限，并将`DENY`所有权限授予被锁定的用户。该`account_lockout_flag`属性与`User`实体相关联，在多个会话中主动撤消该标志`User`之前一直有效。该示例使用`when`条件进行评估`account_lockout_flag`。

该示例还添加了有关请求和会话的详细信息。上下文信息指定已使用多因素身份验证对会话进行身份验证。为了实现此验证，该示例使用`when`条件来评估作为上下文字段一部分的`uses_mfa`标志。有关添加上下文的最佳做法的更多信息，请参阅 [Cedar 文档](https://docs.cedarpolicy.com/auth/entities-syntax.html)。

```
permit (
    principal in MultitenantApp::Role::"allAccessRole",
    action in [
        MultitenantApp::Action::"viewData",
        MultitenantApp::Action::"updateData"
    ],
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

除非资源与请求委托人的`Tenant`属性属于同一组，否则此策略禁止访问资源。这种维护租户隔离的方法被称为 “*一个共享的多租户策略存储*” 方法。有关多租户 SaaS 应用程序的已验证权限设计注意事项的更多信息，请参阅[已验证权限多租户设计注意事项部分](avp-design-considerations.md)。

该政策还确保委托人是`allAccessRole`和的成员，并将操作限制为`viewData`和。`updateData`此外，此策略还会验证即`account_lockout_flag`为`false`以及上下文值的`uses_mfa`计算结果是否为。`true`

同样，以下策略可确保委托人和资源都与同一个租户相关联，从而防止跨租户访问。该政策还确保委托人是其成员，`viewDataRole`并将操作限制为。`viewData`此外，它还会验证是否`account_lockout_flag`为，`false`以及的上下文值的`uses_mfa`计算结果是否为。`true`

```
permit (
    principal in MultitenantApp::Role::"viewDataRole",
    action == MultitenantApp::Action::"viewData",
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

第三个策略与前一个策略类似。该策略要求资源必须是与所代表的实体相对应的组的成员`principal.Tenant`。这样可以确保委托人和资源都与租户 B 相关联，从而防止跨租户访问。该政策确保委托人是其成员，`updateDataRole`并限制其操作。`updateData`此外，此策略还会验证是否`account_lockout_flag`为，`false`以及的上下文值是否`uses_mfa`计算为。`true`

```
permit (
    principal in MultitenantApp::Role::"updateDataRole",
    action == MultitenantApp::Action::"updateData",
    resource
)
when {
    principal.account_lockout_flag == false &&
    context.uses_mfa == true &&
    resource in principal.Tenant
};
```

以下授权请求由本节前面讨论的三个策略进行评估。在此授权请求中，类型为`User`且值为的委托人向该角色`Alice`发出`updateData`请求`allAccessRole`。 `Alice`的属性的`Tenant`值为`Tenant::"TenantA"`。正在尝试执行的操作`Alice`是，`updateData,`并且要应用该操作`SampleData`的资源属于该类型`Data`。 `SampleData`已`TenantA`作为父实体。

根据策略存储中的第一个`<DATAMICROSERVICE_POLICYSTOREID>`策略，`Alice`可以对资源执行操作，前提是满足策略`when`条款中的条件。`updateData`第一个条件要求`principal.Tenant`属性计算为`TenantA`。第二个条件要求委托人的属性`account_lockout_flag`为`false`。最后一个条件要求上下文`uses_mfa`必须是`true`。由于所有三个条件都已满足，因此请求会返回一个`ALLOW`决定。

```
{
  "policyStoreId": "DATAMICROSERVICE_POLICYSTORE",
  "principal": {
      "entityType": "MultitenantApp::User",
      "entityId": "Alice"
  },
  "action": {
      "actionType": "MultitenantApp::Action",
      "actionId": "updateData"
  },
  "resource": {
      "entityType": "MultitenantApp::Data",
      "entityId": "SampleData"
  },
  "context": {
    "contextMap": {
        "uses_mfa": {
            "boolean": true
        }
    }
  },
  "entities": {
    "entityList": [
      {
        "identifier": {
            "entityType": "MultitenantApp::User",
            "entityId": "Alice"
        },
        "attributes": {
            {
                "account_lockout_flag": {
                    "boolean": false
                },
                "Tenant": {
                   "entityIdentifier": {
                        "entityType":"MultitenantApp::Tenant",
                        "entityId":"TenantA"
                   }
                }
            }
        },
        "parents": [
            {
                "entityType": "MultitenantApp::Role",
                "entityId": "allAccessRole"
            }
        ]
        },
     {
        "identifier": {
            "entityType": "MultitenantApp::Data",
            "entityId": "SampleData"
        },
        "attributes": {},
        "parents": [
            {
                "entityType": "MultitenantApp::Tenant",
                "entityId": "TenantA"
            }
        ]
      }
    ]
  }
}
```

# 示例 5：使用已验证权限和 Cedar 进行用户界面筛选
<a name="avp-ui-filtering-examples"></a>

您还可以使用已验证的权限根据授权的操作对用户界面元素实施 RBAC 筛选。对于具有上下文敏感用户界面元素的应用程序来说，这非常有价值，这些元素可能与特定用户或租户相关联，如果是多租户 SaaS 应用程序。

在以下示例中`Users`，`Role``viewer`不允许执行更新。对于这些用户，用户界面不应呈现任何更新按钮。

![\[使用 Amazon 验证权限和 Cedar 进行用户界面筛选示例\]](http://docs.aws.amazon.com/zh_cn/prescriptive-guidance/latest/saas-multitenant-api-access-authorization/images/avp-example-5.png)


在此示例中，单页 Web 应用程序有四个按钮。哪些按钮可见取决于当前登录应用程序的用户。`Role`在单页 Web 应用程序呈现 UI 时，它会查询 “已验证权限” 以确定用户有权执行哪些操作，然后根据授权决定生成按钮。

以下策略指定值为`Role`的类型`viewer`可以同时查看用户和数据。此策略的`ALLOW`授权决策需要`viewData`或`viewUsers`操作，还需要将资源与`Data`或类型相关联`Users`。`ALLOW`决定允许 UI 呈现两个按钮：`viewDataButton`和`viewUsersButton`。

```
permit (
    principal in GuiAPP::Role::"viewer",
    action in [GuiAPP::Action::"viewData", GuiAPP::Action::"viewUsers"],
    resource 
)
when {
   resource in [GuiAPP::Type::"Data", GuiAPP::Type::"Users"]
};
```

以下策略指定值为`Role`的类型`viewerDataOnly`只能查看数据。此策略的`ALLOW`授权决策需要`viewData`采取行动，还需要与该类型关联的资源`Data`。`ALLOW`决定允许 UI 呈现按钮`viewDataButton`。

```
permit (
    principal in GuiApp::Role::"viewerDataOnly",
    action in [GuiApp::Action::"viewData"],
    resource in [GuiApp::Type::"Data"] 
);
```

以下策略指定值为`Role`的类型`admin`可以编辑和查看数据和用户。此策略的`ALLOW`授权决策需要`updateData``updateUsers`、`viewData,`或的操作`viewUsers`，并且还需要将资源与`Data`或类型相关联`Users`。`ALLOW`决定允许 UI 呈现所有四个按钮：`updateDataButton``updateUsersButton`、`viewDataButton`、和`viewUsersButton`。

```
permit (
    principal in GuiApp::Role::"admin",
    action in [
        GuiApp::Action::"updateData",
        GuiApp::Action::"updateUsers",
        GuiApp::Action::"viewData", 
        GuiApp::Action::"viewUsers"
       ],
    resource 
)
when {
   resource in [GuiApp::Type::"Data", GuiApp::Type::"Users"]
};
```