使用 API Gateway Lambda 授权方 - Amazon API Gateway
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

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

使用 API Gateway Lambda 授权方

Lambda 授权方(以前称为自定义授权方)是一种 API Gateway 特征,该功能使用 Lambda 函数来控制对 API 的访问。

如果您要实施使用持有者令牌身份验证策略(如 OAuth 或 SAML)或使用请求参数来确定调用方的身份的自定义授权架构,Lambda 授权方将很有用。

当客户端向您的 API 的方法之一发出请求时,API Gateway 将调用您的 Lambda 授权方,从而采用调用方的身份作为输入并返回 IAM 策略作为输出。

Lambda 授权方有两种类型:

  • 基于令牌的 Lambda 授权方(又称为 TOKEN 授权方)接收持有者令牌中的调用方身份,如 JSON Web 令牌 (JWT) 或 OAuth 令牌。有关示例应用程序,请参阅巴西开放银行——授权示例 GitHub。

  • 基于请求参数 的 Lambda 授权方(又称为 REQUEST 授权方)接收标头、查询字符串参数、stageVariables$context 变量的组合中的调用方身份。

    对于 WebSocket API,仅支持基于请求参数的授权器。

可以使用与您创建 API 时不同的 Amazon 账户中的 Amazon Lambda 函数。有关更多信息,请参阅配置跨账户 Lambda 授权方

例如 Lambda 函数,请参阅上的 aws-apigateway-lambda-authorizer- blueprints。 GitHub

Lambda 授权方身份验证工作流程

下图说明了 Lambda 授权方的授权工作流程。

API Gateway Lambda 授权工作流程
  1. 客户端对 API Gateway API 方法调用方法,同时传递持有者令牌或请求参数。

  2. API Gateway 检查是否为该方法配置了 Lambda 授权方。如果是,API Gateway 调用 Lambda 函数。

  3. Lambda 函数通过以下方式对调用方进行身份验证:

    • 调用 OAuth 提供商以获取 OAuth 访问令牌。

    • 调用 SAML 提供商以获取 SAML 断言。

    • 基于请求参数值生成 IAM 策略。

    • 从数据库中检索凭证。

  4. 如果调用成功,Lambda 函数将通过返回一个包含至少一个 IAM 策略和一个委托人标识符的输出对象来授予访问权限。

  5. API Gateway 对策略进行评估。

    • 如果拒绝访问,API Gateway 将返回一个合适的 HTTP 状态代码,如 403 ACCESS_DENIED

    • 如果允许访问,API Gateway 将调用该方法。如果在授权方设备中启用了缓存,API Gateway 还将缓存策略,以便 Lambda 授权方函数无需再次进行调用。

  6. 如果 Lambda 函数返回响应,则调用可能会失败。401 Unauthorized您可以自定义401 Unauthorized网关响应。要了解更多信息,请参阅API Gateway 中的网关响应

创建 API Gateway Lambda 授权方的步骤

要创建 Lambda 授权方,您需要执行以下任务:

  1. 在 Lambda 控制台中创建 Lambda 授权方函数,如 在 Lambda 控制台中创建 API Gateway Lambda 授权方函数中所述。您可以使用其中一个蓝图示例作为起点并根据需要自定义输入输出

  2. 将 Lambda 函数作为 API Gateway Authorizer 并将 API 方法配置为需要它,如使用 API Gateway 控制台配置 Lambda 授权方中所述。或者,如果您需要跨账户 Lambda 授权方,请参阅配置跨账户 Lambda 授权方

    注意

    您也可以使用 Amazon CLI 或 Amazon SDK 配置授权方。

  3. 通过使用 Postman 测试您的授权方,如使用 API Gateway Lambda 授权方调用 API中所述。

在 Lambda 控制台中创建 API Gateway Lambda 授权方函数

在配置 Lambda 授权方之前,您必须先创建用于实施逻辑以对调用方进行授权和(如有必要)身份验证的 Lambda 函数。Lambda 控制台提供了 Python 蓝图,您可以通过选择使用蓝图并选择蓝图来使用api-gateway-authorizer-python蓝图。否则,您需要使用 awslabs GitHub 存储库中的一个蓝图作为起点。

对于此部分中的示例 Lambda 授权方函数(未调用其他服务),您可以使用内置的 AWSLambdaBasicExecutionRole。在为您自己的 API Gateway Lambda 授权方创建 Lambda 函数时,如果该 Lambda 函数调用其他服务,则需要为 Lambda 函数分配一个 IAM 执行角色。 Amazon 要创建该角色,请按照 Amazon Lambda 执行角色中的说明操作。

有关 Lambda 函数的更多示例,请参阅上的 aws-apigateway-lambda-authorizer- blueprints。 GitHub有关示例应用程序,请参阅巴西开放银行——授权示例 GitHub。

示例:创建基于令牌的 Lambda 授权方函数

要创建基于令牌的 Lambda 授权方函数,请在 Lambda 控制台中,对于最新的运行时系统输入以下 Node.js 代码。然后,在 API Gateway 控制台中测试授权方。

创建基于令牌的 Lambda 授权方函数
  1. 在 Lambda 控制台中,选择创建函数

  2. 选择 Author from scratch (从头开始创作)

  3. 输入函数的名称。

  4. 运行时中,选择受支持的最新 Node.jsPython 运行时。

  5. 选择创建函数

  6. 在编码编辑器中复制/粘贴以下代码。

    Node.js
    // A simple token-based authorizer example to demonstrate how to use an authorization token // to allow or deny a request. In this example, the caller named 'user' is allowed to invoke // a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke // the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty // string, the authorizer function returns an HTTP 401 status code. For any other token value, // the authorizer returns an HTTP 500 status code. // Note that token values are case-sensitive. export const handler = function(event, context, callback) { var token = event.authorizationToken; switch (token) { case 'allow': callback(null, generatePolicy('user', 'Allow', event.methodArn)); break; case 'deny': callback(null, generatePolicy('user', 'Deny', event.methodArn)); break; case 'unauthorized': callback("Unauthorized"); // Return a 401 Unauthorized response break; default: callback("Error: Invalid token"); // Return a 500 Invalid token response } }; // Help function to generate an IAM policy var generatePolicy = function(principalId, effect, resource) { var authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement[0] = statementOne; authResponse.policyDocument = policyDocument; } // Optional output with custom properties of the String, Number or Boolean type. authResponse.context = { "stringKey": "stringval", "numberKey": 123, "booleanKey": true }; return authResponse; }
    Python
    # A simple token-based authorizer example to demonstrate how to use an authorization token # to allow or deny a request. In this example, the caller named 'user' is allowed to invoke # a request if the client-supplied token value is 'allow'. The caller is not allowed to invoke # the request if the token value is 'deny'. If the token value is 'unauthorized' or an empty # string, the authorizer function returns an HTTP 401 status code. For any other token value, # the authorizer returns an HTTP 500 status code. # Note that token values are case-sensitive. import json def lambda_handler(event, context): token = event['authorizationToken'] if token == 'allow': print('authorized') response = generatePolicy('user', 'Allow', event['methodArn']) elif token == 'deny': print('unauthorized') response = generatePolicy('user', 'Deny', event['methodArn']) elif token == 'unauthorized': print('unauthorized') raise Exception('Unauthorized') # Return a 401 Unauthorized response return 'unauthorized' try: return json.loads(response) except BaseException: print('unauthorized') return 'unauthorized' # Return a 500 error def generatePolicy(principalId, effect, resource): authResponse = {} authResponse['principalId'] = principalId if (effect and resource): policyDocument = {} policyDocument['Version'] = '2012-10-17' policyDocument['Statement'] = [] statementOne = {} statementOne['Action'] = 'execute-api:Invoke' statementOne['Effect'] = effect statementOne['Resource'] = resource policyDocument['Statement'] = [statementOne] authResponse['policyDocument'] = policyDocument authResponse['context'] = { "stringKey": "stringval", "numberKey": 123, "booleanKey": True } authResponse_JSON = json.dumps(authResponse) return authResponse_JSON
  7. 选择 Deploy (部署)

在创建 Lambda 函数后,您需要在 API Gateway 控制台中创建基于令牌的 Lambda 授权方并进行测试。

创建基于令牌的 Lambda 授权方
  1. 在 API Gateway 控制台中,创建一个简单的 API(如果您还没有这样做)。

  2. 从 API 列表中选择您的 API。

  3. 选择 Authorizers (授权方)

  4. 选择 Create Authorizer(创建授权方)。

  5. 授权方名称中,输入名称。

  6. 对于授权方类型,选择 Lambda

  7. 对于 Lambda 函数,请选择您创建 Lambda 授权方函数 Amazon Web Services 区域 的位置,然后输入函数名称。

  8. Lambda 调用角色留空。

  9. 对于 Lambda 事件负载,选择令牌

  10. 对于令牌来源,输入 authorizationToken

  11. 选择 Create Authorizer(创建授权方)。

测试授权方
  1. 选择授权方的名称。

  2. 测试授权方下,对于 authorizationToken 值,输入 allow

  3. 选择测试授权方

在此示例中,当 API 接收方法请求时,API Gateway 将源令牌传递到 event.authorizationToken 属性中的此 Lambda 授权方函数。Lambda 授权方函数将读取令牌并按下所示做出行为:

  • 如果令牌值为 'allow',授权方函数的测试将返回 200 OK HTTP 响应和 IAM 策略(类似于以下内容)并且方法请求成功:

    { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow", "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/" } ] }
  • 如果令牌值为 'deny',授权方函数的测试将返回 200 OK HTTP 响应和 Deny IAM 策略(类似于以下内容)并且方法请求失败:

    { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Deny", "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/" } ] }
    注意

    在测试环境之外,授权方函数将返回 403 Forbidden HTTP 响应并且方法请求失败。

  • 如果令牌值为 'unauthorized' 或空字符串,授权方函数的测试将返回 401 Unauthorized HTTP 响应并且方法调用失败。

  • 如果令牌为任何其他内容,客户端将收到 500 Invalid token 响应并且方法调用失败。

注意

在生产代码中,您可能需要先对用户进行身份验证,然后才能授予授权。如果这样,您也可以通过调用身份验证提供商(如该提供商的文档中的指示)在 Lambda 函数中添加身份验证逻辑。

除了返回 IAM 策略之外,Lambda 授权方函数还必须返回调用方的委托人标识符。它还可以选择返回一个 context 对象(包含可传入集成后端的其他信息)。有关更多信息,请参阅来自 Amazon API Gateway Lambda 授权方的输出

示例:创建基于请求的 Lambda 授权方函数

要创建基于请求的 Lambda 授权方函数,请在 Lambda 控制台中,对于最新的运行时系统输入以下 Node.js 代码。然后,在 API Gateway 控制台中测试授权方。

  1. 在 Lambda 控制台中,选择创建函数

  2. 选择 Author from scratch (从头开始创作)

  3. 输入函数的名称。

  4. 运行时中,选择受支持的最新 Node.jsPython 运行时。

  5. 选择创建函数

  6. 在编码编辑器中复制/粘贴以下代码。

    Node.js
    // A simple request-based authorizer example to demonstrate how to use request // parameters to allow or deny a request. In this example, a request is // authorized if the client-supplied headerauth1 header, QueryString1 // query parameter, and stage variable of StageVar1 all match // specified values of 'headerValue1', 'queryValue1', and 'stageValue1', // respectively. export const handler = function(event, context, callback) { console.log('Received event:', JSON.stringify(event, null, 2)); // Retrieve request parameters from the Lambda function input: var headers = event.headers; var queryStringParameters = event.queryStringParameters; var pathParameters = event.pathParameters; var stageVariables = event.stageVariables; // Parse the input for the parameter values var tmp = event.methodArn.split(':'); var apiGatewayArnTmp = tmp[5].split('/'); var awsAccountId = tmp[4]; var region = tmp[3]; var restApiId = apiGatewayArnTmp[0]; var stage = apiGatewayArnTmp[1]; var method = apiGatewayArnTmp[2]; var resource = '/'; // root resource if (apiGatewayArnTmp[3]) { resource += apiGatewayArnTmp[3]; } // Perform authorization to return the Allow policy for correct parameters and // the 'Unauthorized' error, otherwise. var authResponse = {}; var condition = {}; condition.IpAddress = {}; if (headers.headerauth1 === "headerValue1" && queryStringParameters.QueryString1 === "queryValue1" && stageVariables.StageVar1 === "stageValue1") { callback(null, generateAllow('me', event.methodArn)); } else { callback("Unauthorized"); } } // Help function to generate an IAM policy var generatePolicy = function(principalId, effect, resource) { // Required output: var authResponse = {}; authResponse.principalId = principalId; if (effect && resource) { var policyDocument = {}; policyDocument.Version = '2012-10-17'; // default version policyDocument.Statement = []; var statementOne = {}; statementOne.Action = 'execute-api:Invoke'; // default action statementOne.Effect = effect; statementOne.Resource = resource; policyDocument.Statement[0] = statementOne; authResponse.policyDocument = policyDocument; } // Optional output with custom properties of the String, Number or Boolean type. authResponse.context = { "stringKey": "stringval", "numberKey": 123, "booleanKey": true }; return authResponse; } var generateAllow = function(principalId, resource) { return generatePolicy(principalId, 'Allow', resource); } var generateDeny = function(principalId, resource) { return generatePolicy(principalId, 'Deny', resource); }
    Python
    # A simple request-based authorizer example to demonstrate how to use request # parameters to allow or deny a request. In this example, a request is # authorized if the client-supplied headerauth1 header, QueryString1 # query parameter, and stage variable of StageVar1 all match # specified values of 'headerValue1', 'queryValue1', and 'stageValue1', # respectively. import json def lambda_handler(event, context): print(event) # Retrieve request parameters from the Lambda function input: headers = event['headers'] queryStringParameters = event['queryStringParameters'] pathParameters = event['pathParameters'] stageVariables = event['stageVariables'] # Parse the input for the parameter values tmp = event['methodArn'].split(':') apiGatewayArnTmp = tmp[5].split('/') awsAccountId = tmp[4] region = tmp[3] restApiId = apiGatewayArnTmp[0] stage = apiGatewayArnTmp[1] method = apiGatewayArnTmp[2] resource = '/' if (apiGatewayArnTmp[3]): resource += apiGatewayArnTmp[3] # Perform authorization to return the Allow policy for correct parameters # and the 'Unauthorized' error, otherwise. authResponse = {} condition = {} condition['IpAddress'] = {} if (headers['headerauth1'] == "headerValue1" and queryStringParameters["QueryString1"] == "queryValue1" and stageVariables["StageVal1"] == "stageValue1"): response = generateAllow('me', event['methodArn']) print('authorized') return json.loads(response) else: print('unauthorized') raise Exception('Unauthorized') # Return a 401 Unauthorized response return 'unauthorized' # Help function to generate IAM policy def generatePolicy(principalId, effect, resource): authResponse = {} authResponse['principalId'] = principalId if (effect and resource): policyDocument = {} policyDocument['Version'] = '2012-10-17' policyDocument['Statement'] = [] statementOne = {} statementOne['Action'] = 'execute-api:Invoke' statementOne['Effect'] = effect statementOne['Resource'] = resource policyDocument['Statement'] = [statementOne] authResponse['policyDocument'] = policyDocument authResponse['context'] = { "stringKey": "stringval", "numberKey": 123, "booleanKey": True } authResponse_JSON = json.dumps(authResponse) return authResponse_JSON def generateAllow(principalId, resource): return generatePolicy(principalId, 'Allow', resource) def generateDeny(principalId, resource): return generatePolicy(principalId, 'Deny', resource)
  7. 选择 Deploy (部署)

在创建 Lambda 函数后,您需要在 API Gateway 控制台中创建基于请求的 Lambda 授权方并进行测试。

创建基于请求的 Lambda 授权方
  1. 在 API Gateway 控制台中,创建一个简单的 API(如果您还没有这样做)。

  2. 从 API 列表中选择您的 API。

  3. 选择 Authorizers (授权方)

  4. 选择 Create Authorizer(创建授权方)。

  5. 授权方名称中,输入名称。

  6. 对于授权方类型,选择 Lambda

  7. 对于 Lambda 函数,请选择您创建 Lambda 授权方函数 Amazon Web Services 区域 的位置,然后输入函数名称。

  8. Lambda 调用角色留空。

  9. 对于 Lambda 事件负载,选择请求

  10. 身份源类型下,输入以下内容:

    1. 选择标头并输入 headerauth1,然后选择添加参数

    2. 身份源类型下,选择查询字符串并输入 QueryString1,然后选择添加参数

    3. 身份源类型下,选择阶段变量并输入 StageVar1

  11. 选择 Create Authorizer(创建授权方)。

测试授权方
  1. 选择授权方的名称。

  2. 测试授权方下,输入以下内容:

    1. 选择标头并输入 headerValue1,然后选择添加参数

    2. 身份源类型下,选择查询字符串并输入 queryValue1,然后选择添加参数

    3. 身份源类型下,选择阶段变量并输入 stageValue1

  3. 选择测试授权方

在此示例中,Lambda 授权方函数将检查输入参数并按如下所示操作:

  • 如果所有必需的参数值匹配预期值,该授权方函数将返回 200 OK HTTP 响应和 IAM 策略(类似于以下内容)并且方法请求成功:

    { "Version": "2012-10-17", "Statement": [ { "Action": "execute-api:Invoke", "Effect": "Allow", "Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/" } ] }
  • 否则,授权方函数将返回 401 Unauthorized HTTP 响应并且方法调用失败。

注意

在生产代码中,您可能需要先对用户进行身份验证,然后才能授予授权。如果这样,您也可以通过调用身份验证提供商(如该提供商的文档中的指示)在 Lambda 函数中添加身份验证逻辑。

除了返回 IAM 策略之外,Lambda 授权方函数还必须返回调用方的委托人标识符。它还可以选择返回一个 context 对象(包含可传入集成后端的其他信息)。有关更多信息,请参阅来自 Amazon API Gateway Lambda 授权方的输出