

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

# 使用令牌验证 AWS Lambda
<a name="reference-smart-on-fhir-token-validation"></a>

创建启用 FHIR 的 HealthLake SMART 数据存储时，必须在请求中`CreateFHIRDatastore`提供 AWS Lambda 该函数的 ARN。Lambda 函数的 ARN 是使用参数在`IdentityProviderConfiguration`对象中指定的。`IdpLambdaArn`

在创建 SMART on 启用 FHIR 的数据存储之前，必须创建 Lambda 函数。一旦创建了数据存储，就无法更改 Lambda ARN。要查看您在创建数据存储时指定的 Lambda ARN，请使用 API 操作。`DescribeFHIRDatastore`

**要在启用了 SMART on FHIR 的数据存储上成功发出 FHIR REST 请求，您的 Lambda 函数必须执行以下操作：**
+ 在不到 1 秒的时间内向 HealthLake 数据存储端点返回响应。
+ 对客户端应用程序发送的 REST API 请求的授权标头中提供的访问令牌进行解码。
+ 分配一个具有足够权限来执行 FHIR REST API 请求的 IAM 服务角色。
+ 完成 FHIR REST API 请求需要以下声明。要了解更多信息，请参阅[所需声明](reference-smart-on-fhir-authentication.md#server-response)。
  + `nbf`
  + `exp`
  + `isAuthorized`
  + `aud`
  + `scope`

使用 Lambda 时，除了您的 Lambda 函数外，您还需要创建执行角色和基于资源的策略。Lambda 函数的执行角色是一个 IAM 角色，它向函数授予访问运行时所需的 AWS 服务和资源的权限。您提供的基于资源的策略必须 HealthLake 允许代表您调用您的函数。

本主题中的各节描述了来自客户端应用程序的示例请求和解码后的响应、创建 Lamb AWS da 函数所需的步骤以及如何创建可以假设的基于资源的策略 HealthLake 。
+ [第 1 部分：创建 Lambda 函数](#smart-on-fhir-lambda-create)
+ [第 2 部分：创建 AWS Lambda 函数使用的 HealthLake 服务角色](#smart-on-fhir-lambda-service-role)
+ [第 3 部分：更新 Lambda 函数的执行角色](#smart-on-fhir-lambda-service-role-execution-role)
+ [第 4 部分：向您的 Lambda 函数添加资源策略](#smart-on-fhir-lambda-invoke-healthlake)
+ [第 5 部分：为您的 Lambda 函数配置并发性](#smart-on-fhir-lambda-function-scaling)

## 创建 AWS Lambda 函数
<a name="smart-on-fhir-lambda-create"></a>

本主题中创建的 Lambda 函数在 HealthLake 收到对启用 FHIR 的 SMART 数据存储的请求时触发。来自客户端应用程序的请求包含一个 REST API 调用和一个包含访问令牌的授权标头。

```
GET https://healthlake.region.amazonaws.com/datastore/datastoreId/r4/
Authorization: Bearer i8hweunweunweofiwweoijewiwe
```

本主题中的示例 Lambda 函数 AWS Secrets Manager 用于掩盖与授权服务器相关的证书。我们强烈建议不要直接在 Lambda 函数中提供授权服务器登录详细信息。

**Example 验证包含授权持有者令牌的 FHIR REST 请求**  
示例 Lambda 函数向您展示了如何验证在启用 FHIR 的数据存储上发送到 SMART 的 FHIR REST 请求。要查看有关如何实现此 Lambda 函数的 step-by-steps说明，请参阅。[使用创建 Lambda 函数 AWS 管理控制台](#create-lambda-console)  
如果 FHIR REST API 请求不包含有效的数据存储终端节点、访问令牌和 REST 操作，则 Lambda 函数将失败。要了解有关所需授权服务器元素的更多信息，请参阅[所需声明](reference-smart-on-fhir-authentication.md#server-response)。  

```
import base64
import boto3
import logging
import json
import os
from urllib import request, parse

logger = logging.getLogger()
logger.setLevel(logging.INFO)

## Uses Secrets manager to gain access to the access key ID and secret access key for the authorization server
client = boto3.client('secretsmanager', region_name="region-of-datastore")
response = client.get_secret_value(SecretId='name-specified-by-customer-in-secretsmanager')
secret = json.loads(response['SecretString'])
client_id = secret['client_id']
client_secret = secret['client_secret']


unencoded_auth = f'{client_id}:{client_secret}'
headers = {
  'Authorization': f'Basic {base64.b64encode(unencoded_auth.encode()).decode()}',
  'Content-Type': 'application/x-www-form-urlencoded'
}

auth_endpoint = os.environ['auth-server-base-url'] # Base URL of the Authorization server
user_role_arn = os.environ['iam-role-arn'] # The IAM role client application will use to complete the HTTP request on the datastore

def lambda_handler(event, context):
    if 'datastoreEndpoint' not in event or 'operationName' not in event or 'bearerToken' not in event:
    return {}

    datastore_endpoint = event['datastoreEndpoint']
    operation_name = event['operationName']
    bearer_token = event['bearerToken']
    logger.info('Datastore Endpoint [{}], Operation Name: [{}]'.format(datastore_endpoint, operation_name))

    ## To validate the token
    auth_response = auth_with_provider(bearer_token)
    logger.info('Auth response: [{}]'.format(auth_response))
    auth_payload = json.loads(auth_response)
    ## Required parameters needed to be sent to the datastore endpoint for the HTTP request to go through
    auth_payload["isAuthorized"] = bool(auth_payload["active"])
    auth_payload["nbf"] = auth_payload["iat"]
    return {"authPayload": auth_payload, "iamRoleARN": user_role_arn}

## access the server
def auth_with_provider(token):
    data = {'token': token, 'token_type_hint': 'access_token'}
    req = request.Request(url=auth_endpoint + '/v1/introspect', data=parse.urlencode(data).encode(), headers=headers)
    with request.urlopen(req) as resp:
    return resp.read().decode()
```

### 使用创建 Lambda 函数 AWS 管理控制台
<a name="create-lambda-console"></a>

以下过程假设您已经创建了在支持 SMART on FHIR 的数据存储上处理 FHIR REST API 请求时要 HealthLake 代入的服务角色。如果您尚未创建服务角色，则仍然可以创建 Lambda 函数。在 Lambda 函数起作用之前，您必须添加服务角色的 ARN。要了解有关创建服务角色并在 Lambda 函数中指定该角色的更多信息，请参阅 [创建用于解码 JWT 的 AWS Lambda 函数的 HealthLake 服务角色](#smart-on-fhir-lambda-service-role)

**创建 Lambda 函数 ()AWS 管理控制台**

1. 打开 Lamba 控制台的 [Functions page](https://console.aws.amazon.com/lambda/home/functions)（函数页面）。

1. 选择**创建函数**。

1. 选择**从头开始编写**。

1. 在**基本信息**下输入**函数名称**。在**运行时**下，选择基于 python 的运行时。

1. 在 **Execution Role**（执行角色）中，选择 **Create a new role with basic Lambda permissions**（创建具有基本 Lambda 权限的新角色）。

   Lambda 创建了一个[执行角色，该角色](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html)向该函数授予将日志上传到亚马逊的权限。 CloudWatchLambda 函数在您调用函数时担任执行角色，并使用执行角色为 SDK 创建证书。 AWS 

1. 选择**代码**选项卡，然后添加示例 Lambda 函数。

   如果您尚未为 Lambda 函数创建要使用的服务角色，则需要先创建该角色，然后示例 Lambda 函数才能运行。要了解有关为 Lambda 函数创建服务角色的更多信息，请参阅。[创建用于解码 JWT 的 AWS Lambda 函数的 HealthLake 服务角色](#smart-on-fhir-lambda-service-role)

   ```
   import base64
   import boto3
   import logging
   import json
   import os
   from urllib import request, parse
   
   logger = logging.getLogger()
   logger.setLevel(logging.INFO)
   
   ## Uses Secrets manager to gain access to the access key ID and secret access key for the authorization server
   client = boto3.client('secretsmanager', region_name="region-of-datastore")
   response = client.get_secret_value(SecretId='name-specified-by-customer-in-secretsmanager')
   secret = json.loads(response['SecretString'])
   client_id = secret['client_id']
   client_secret = secret['client_secret']
   
   
   unencoded_auth = f'{client_id}:{client_secret}'
   headers = {
     'Authorization': f'Basic {base64.b64encode(unencoded_auth.encode()).decode()}',
     'Content-Type': 'application/x-www-form-urlencoded'
   }
   
   auth_endpoint = os.environ['auth-server-base-url'] # Base URL of the Authorization server
   user_role_arn = os.environ['iam-role-arn'] # The IAM role client application will use to complete the HTTP request on the datastore
   
   def lambda_handler(event, context):
       if 'datastoreEndpoint' not in event or 'operationName' not in event or 'bearerToken' not in event:
       return {}
   
       datastore_endpoint = event['datastoreEndpoint']
       operation_name = event['operationName']
       bearer_token = event['bearerToken']
       logger.info('Datastore Endpoint [{}], Operation Name: [{}]'.format(datastore_endpoint, operation_name))
   
       ## To validate the token
       auth_response = auth_with_provider(bearer_token)
       logger.info('Auth response: [{}]'.format(auth_response))
       auth_payload = json.loads(auth_response)
       ## Required parameters needed to be sent to the datastore endpoint for the HTTP request to go through
       auth_payload["isAuthorized"] = bool(auth_payload["active"])
       auth_payload["nbf"] = auth_payload["iat"]
       return {"authPayload": auth_payload, "iamRoleARN": user_role_arn}
   
   ## Access the server
   def auth_with_provider(token):
       data = {'token': token, 'token_type_hint': 'access_token'}
       req = request.Request(url=auth_endpoint + '/v1/introspect', data=parse.urlencode(data).encode(), headers=headers)
       with request.urlopen(req) as resp:
       return resp.read().decode()
   ```

### 修改 Lambda 函数的执行角色
<a name="modify-lambda-execution-role"></a>

创建 Lambda 函数后，您需要更新执行角色以包含调用 Secrets Manager 所需的权限。在 Secrets Manager 中，你创建的每个密钥都有一个 ARN。要应用最低权限，执行角色只能访问 Lambda 函数执行所需的资源。

您可以通过在 IAM 控制台中搜索或在 Lambda 控制台中选择**配置**来修改 Lambda 函数的执行角色。要了解有关管理 Lambda 函数执行角色的更多信息，请参阅。[Lambda 执行角色](#smart-on-fhir-lambda-service-role-execution-role)

**Example 授予访问权限的 Lambda 函数执行角色 `GetSecretValue`**  
将 IAM 操作`GetSecretValue`添加到执行角色可授予示例 Lambda 函数运行所需的权限。    
****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:secret-name-DKodTA"
        }
    ]
}
```

此时，您已经创建了一个 Lambda 函数，该函数可用于验证在 FHIR 启用了 FHIR 的数据存储上发送到您的 SMART 的 FHIR REST 请求中提供的访问令牌。

## 创建用于解码 JWT 的 AWS Lambda 函数的 HealthLake 服务角色
<a name="smart-on-fhir-lambda-service-role"></a>

**角色：IAM 管理员**  
可以添加或删除 IAM 策略并创建新 IAM 身份的用户。  

**服务角色**  
 服务角色是由一项服务担任、代表您执行操作的 [IAM 角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html)。IAM 管理员可以在 IAM 中创建、修改和删除服务角色。有关更多信息，请参阅《IAM 用户指南》**中的[创建向 AWS 服务委派权限的角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-service.html)。

解码 JSON 网络令牌 (JWT) 后，Lambda 还需要返回 IAM 角色 ARN 的授权。此角色必须具有执行 REST API 请求所需的权限，否则将因权限不足而失败。

使用 IAM 设置自定义策略时，最好授予所需的最低权限。要了解更多信息，请参阅 *IAM 用户*指南中的[应用最低权限权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)。

创建要在授权 Lambda 函数中指定的 HealthLake 服务角色需要两个步骤。
+ 首先，您需要创建 IAM 策略。该策略必须指定对您在授权服务器中为其提供作用域的 FHIR 资源的访问权限。
+ 其次，您需要创建服务角色。创建角色时，您可以指定信任关系并附加您在第一步中创建的策略。信任关系指定 HealthLake 为服务主体。您需要在此步骤中指定 HealthLake 数据存储 ARN 和 AWS 账户 ID。

### 创建新的 IAM 策略
<a name="lambda-service-role-part-1"></a>

您在授权服务器中定义的范围决定了经过身份验证的用户在 HealthLake 数据存储中可以访问哪些 FHIR 资源。

您创建的 IAM 策略可以根据您定义的范围进行定制。

可以在 IAM 策略声明的`Action`元素中定义以下操作。您可以为表`Action`中的每一个定义一个`Resource types`。数据存储是唯一可以在 HealthLake IAM 权限策略声明的`Resource`元素中定义的受支持的资源类型。

单个 FHIR 资源不是您可以在 IAM 权限策略中定义为元素的资源。


**操作定义为 HealthLake**  

| 操作 | 描述 | 访问级别 | 资源类型（必填） | 
| --- | --- | --- | --- | 
| CreateResource | 向创建资源授予权限 | 写入 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| DeleteResource | 授予删除资源的权限 | 写入 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| ReadResource | 授予读取资源的权限 | 读取 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| SearchWithGet | 授予使用 GET 方法搜索资源的权限 | 读取 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| SearchWithPost | 授予使用 POST 方法搜索资源的权限 | 读取 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| 开始 FHIRExport JobWithPost | 授予使用 GET 开始 FHIR 导出任务的权限 | 写入 | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 
| UpdateResource | 授予更新资源的权限 | 写入  | 数据存储 ARN：arn: aws: healthlake::: datastore/fhir/ your-region 111122223333 your-datastore-id | 

要开始使用，你可以使用`AmazonHealthLakeFullAccess`。该策略将允许对数据存储中找到的所有 FHIR 资源进行读取、写入、搜索和导出。要授予对数据存储的只读权限，请使用`AmazonHealthLakeReadOnlyAccess`。

要了解有关使用、或 IAM 创建自定义策略的更多信息 AWS 管理控制台 AWS CLI SDKs，请参阅 IA [M 用户指南中的创建](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html) *IAM* 策略。

### 为 HealthLake (IAM 控制台) 创建服务角色
<a name="lambda-service-role-part-2"></a>

使用此过程创建服务角色。创建服务时，还需要指定 IAM 策略。

**为 HealthLake （IAM 控制台）创建服务角色**

1. 登录 AWS 管理控制台 并打开 IAM 控制台，网址为[https://console.aws.amazon.com/iam/](https://console.aws.amazon.com/iam/)。

1. 在 IAM 控制台的导航窗格中，选择**角色**。

1. 然后，选择**创建角色**。

1. 在**选择信任实体**页面上，选择**自定义信任策略**。

1. 接下来，在 “**自定义信任策略**” 下更新示例策略，如下所示。**your-account-id**替换为您的账号，然后添加要在导入或导出任务中使用的数据存储的 ARN。

------
#### [ JSON ]

****  

   ```
   {
       "Version":"2012-10-17",		 	 	 
       "Statement": [
           {
               "Effect": "Allow",
               "Action": "sts:AssumeRole",
               "Principal": {
                   "Service": "healthlake.amazonaws.com"
               },
               "Condition": {
                   "StringEquals": {
                       "aws:SourceAccount": "123456789012"
                   },
                   "ArnEquals": {
                       "aws:SourceArn": "arn:aws:healthlake:us-east-1:123456789012:datastore/fhir/your-datastore-id"
                   }
               }
           }
       ]
   }
   ```

------

1. 然后选择**下一步**。

1. 在**添加权限**页面上，选择您希望 HealthLake 服务采用的策略。要查找您的策略，请在**权限策略**下进行搜索。

1. 然后，选择**附加策略**。

1. 然后在 “**名称、查看和创建**” 页面的 “**角色名称**” 下输入名称。

1. （可选）然后在**描述**下，为您的角色添加简短描述。

1. 如果可能，输入有助于识别该角色的作用的角色名称或角色名称后缀。角色名称在您的 AWS 账户内必须是唯一的。它们不按大小写区分。例如，您无法同时创建名为 **PRODROLE** 和 **prodrole** 的角色。由于多个单位可能引用该角色，角色创建完毕后无法编辑角色名称。

1. 查看角色详细信息，然后选择**创建角色**。

要了解如何在示例 Lambda 函数中指定角色 ARN，请参阅。[创建 AWS Lambda 函数](#smart-on-fhir-lambda-create)

## Lambda 执行角色
<a name="smart-on-fhir-lambda-service-role-execution-role"></a>

Lambda 函数的执行角色是一个 IAM 角色，它向该函数授予访问 AWS 服务和资源的权限。此页面提供有关如何创建、查看和管理 Lambda 函数执行角色的信息。

默认情况下，当您使用创建新的 Lambda 函数时，Lambda 会创建具有最低权限的执行角色。 AWS 管理控制台要管理在执行角色中授予的权限，请参阅 *Lambda 开发人员*[指南中的在 IAM 控制台中创建执行角色](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html#permissions-executionrole-console)。

本主题中提供的示例 Lambda 函数使用 Secrets Manager 来掩盖授权服务器的证书。

与您创建的任何 IAM 角色一样，遵循最低权限最佳实践非常重要。在开发阶段，您有时可能会授予超出所需权限的权限。在生产环境中发布函数之前，最佳实践是调整策略，使其仅包含所需权限。有关更多信息，请参阅 *IAM 用户*[指南中的应用最低权限](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege)。

## HealthLake 允许触发你的 Lambda 函数
<a name="smart-on-fhir-lambda-invoke-healthlake"></a>

因此， HealthLake 可以代表您调用 Lambda 函数，您必须执行以下操作：
+ 您需要将设置为`IdpLambdaArn`等于您要在请求中调用的 Lambda 函数的 ARN。 HealthLake `CreateFHIRDatastore`
+ 您需要一个基于资源的策略， HealthLake 允许您代表您调用 Lambda 函数。

在启用 SMART on FHIR 的数据存储上 HealthLake 收到 FHIR REST API 请求时，它需要权限才能代表您调用创建数据存储时指定的 Lambda 函数。要授予 HealthLake 访问权限，您将使用基于资源的策略。*要详细了解如何为 Lambda 函数创建基于资源的策略，[请参阅开发者指南中的允许 AWS 服务调用 Lambda](https://docs.aws.amazon.com/lambda/latest/dg/access-control-resource-based.html#permissions-resource-serviceinvoke) 函数。AWS Lambda *

## 为您的 Lambda 函数配置并发性
<a name="smart-on-fhir-lambda-function-scaling"></a>

**重要**  
HealthLake 要求您的 Lambda 函数的最大运行时间必须小于一秒（1000 毫秒）。  
如果您的 Lambda 函数超过了运行时间限制，则会出现异常。TimeOut

为避免出现此异常，我们建议配置预配置的并发性。通过在调用增加之前分配预置并发，您可以确保所有请求都由延迟较低的初始化实例来提供。*要了解有关配置预配置并发的更多信息，请参阅 Lambda 开发人员指南[中的配置预配置](https://docs.aws.amazon.com/ambda/latest/dg/provisioned-concurrency.html)并发*

要查看您的 Lambda 函数当前的平均运行时间，请使用 Lambda 控制台上您的 Lambda 函数的 “**监控**” 页面。默认情况下，Lambda 控制台提供**持续时间**图表，显示您的函数代码处理事件所花费的平均时间、最小时间和最大时间。*要了解有关监控 Lambda 函数的更多信息，请参阅 Lambda 开发[人员指南中的 Lambda 控制台中的监控函数](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-functions-access-metrics.html#monitoring-console-graph-types)。*

*如果您已经为 Lambda 函数配置了并发并想要对其进行监控，请参阅 Lambda 开发者指南中的[监控并发](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-concurrency.html)性。*