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

利用 Amazon Lambda REQUEST 授权方来控制对 WebSocket API 的访问

WebSocket API 中的 Lambda 授权方函数类似于 REST API 的授权方函数,但有以下例外情况:

  • 您只能对 $connect 路由使用 Lambda 授权方函数。

  • 您不能使用路径变量 (event.pathParameters),因为路径是固定的。

  • event.methodArn 与 REST API 等效变量不同,因为它没有 HTTP 方法。如果是 $connect,则 methodArn"$connect" 结尾:

    arn:aws:execute-api:region:account-id:api-id/stage-name/$connect
  • event.requestContext 中的上下文变量与 REST API 的上下文变量不同。

以下示例显示了适用于 WebSocket API 的 REQUEST 授权方的输入:

{ "type": "REQUEST", "methodArn": "arn:aws:execute-api:us-east-1:123456789012:abcdef123/default/$connect", "headers": { "Connection": "upgrade", "content-length": "0", "HeaderAuth1": "headerValue1", "Host": "abcdef123.execute-api.us-east-1.amazonaws.com", "Sec-WebSocket-Extensions": "permessage-deflate; client_max_window_bits", "Sec-WebSocket-Key": "...", "Sec-WebSocket-Version": "13", "Upgrade": "websocket", "X-Amzn-Trace-Id": "...", "X-Forwarded-For": "...", "X-Forwarded-Port": "443", "X-Forwarded-Proto": "https" }, "multiValueHeaders": { "Connection": [ "upgrade" ], "content-length": [ "0" ], "HeaderAuth1": [ "headerValue1" ], "Host": [ "abcdef123.execute-api.us-east-1.amazonaws.com" ], "Sec-WebSocket-Extensions": [ "permessage-deflate; client_max_window_bits" ], "Sec-WebSocket-Key": [ "..." ], "Sec-WebSocket-Version": [ "13" ], "Upgrade": [ "websocket" ], "X-Amzn-Trace-Id": [ "..." ], "X-Forwarded-For": [ "..." ], "X-Forwarded-Port": [ "443" ], "X-Forwarded-Proto": [ "https" ] }, "queryStringParameters": { "QueryString1": "queryValue1" }, "multiValueQueryStringParameters": { "QueryString1": [ "queryValue1" ] }, "stageVariables": {}, "requestContext": { "routeKey": "$connect", "eventType": "CONNECT", "extendedRequestId": "...", "requestTime": "19/Jan/2023:21:13:26 +0000", "messageDirection": "IN", "stage": "default", "connectedAt": 1674162806344, "requestTimeEpoch": 1674162806345, "identity": { "sourceIp": "..." }, "requestId": "...", "domainName": "abcdef123.execute-api.us-east-1.amazonaws.com", "connectionId": "...", "apiId": "abcdef123" } }

以下示例 Lambda 授权方函数是其他 Lambda 授权方函数示例中 REST API 的 Lambda 授权方函数的 WebSocket 版本:

Node.js
// A simple REQUEST 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 and QueryString1 query parameter // in the request context match the specified values of // of 'headerValue1' and 'queryValue1' 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 stageVariables = event.stageVariables; var requestContext = event.requestContext; // 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 ApiId = apiGatewayArnTmp[0]; var stage = apiGatewayArnTmp[1]; var route = apiGatewayArnTmp[2]; // 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") { callback(null, generateAllow('me', event.methodArn)); } else { callback("Unauthorized"); } } // Helper 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 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 and QueryString1 query parameter # in the request context match the specified values of # of 'headerValue1' and 'queryValue1' respectively. import json def lambda_handler(event, context): print(event) # Retrieve request parameters from the Lambda function input: headers = event['headers'] queryStringParameters = event['queryStringParameters'] stageVariables = event['stageVariables'] requestContext = event['requestContext'] # Parse the input for the parameter values tmp = event['methodArn'].split(':') apiGatewayArnTmp = tmp[5].split('/') awsAccountId = tmp[4] region = tmp[3] ApiId = apiGatewayArnTmp[0] stage = apiGatewayArnTmp[1] route = apiGatewayArnTmp[2] # 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"): response = generateAllow('me', event['methodArn']) print('authorized') return json.loads(response) else: print('unauthorized') 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)

要将前面的 Lambda 函数配置为 WebSocket API 的 REQUEST 授权方函数,请遵循与 REST API 相同的过程。

要将 $connect 路由配置为在控制台中使用此 Lambda 授权方,请选择或创建 $connect 路由。在路由请求设置部分中,选择编辑。在授权下拉菜单中选择您的授权方,然后选择保存更改

要测试授权方,您将需要创建一个新连接。在 $connect 中更改授权方不会影响已连接的客户端。当您连接到 WebSocket API 时,需要为任何已配置的身份源提供值。例如,您可以通过使用 wscat 发送有效的查询字符串和标头进行连接,如以下示例所示:

wscat -c 'wss://myapi.execute-api.us-east-1.amazonaws.com/beta?QueryString1=queryValue1' -H HeaderAuth1:headerValue1

如果您尝试在没有有效身份值的情况下进行连接,您将收到 401 响应:

wscat -c wss://myapi.execute-api.us-east-1.amazonaws.com/beta error: Unexpected server response: 401