使用 AWS_IAM 授权对 AWS WebSocket API 网关的请求

Authorise Request to AWS WebSocket API Gateway using AWS_IAM

我已经使用 WebSocket 协议设置了一个 API 网关。在“$connect”路由请求设置中,我选择了 'AWS_IAM' 作为授权方式。在用户通过 Cognito 登录后,Web 应用程序需要连接到此 WebSocket API。然后如何在网络应用程序上授权来自 JavaScript 的 WebSocket API 请求?使用 HTTP API 网关,我可以从访问密钥和 session 令牌生成签名,这些令牌已传递到请求 header。但我无法在 WebSocket 请求中传递 headers。

我已从 AWS 支持部门得到答复。我需要签署 wss URL。因此,不是在 HTTP 请求中设置请求 headers,而是将签名信息传递给查询字符串参数中的 url。签名的 wss URL 看起来像:wss://API_ID.execute-api.region.amazonaws.com/dev?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ACCESSKEY/20200131/region/execute-api/aws4_request&X-Amz-Date=20200131T100233Z&X-Amz-Security-Token=SECURITY_TOKEN&X-Amz-SignedHeaders=host&X-Amz-Signature=SIGNATURE.

要生成带符号的 URL,我可以使用 @aws-amplify/core 库中的 Signer.signUrl 方法。

这是一些对我有用的 example/pseudo 代码:

使用 AWS Amplify 认证用户:

import { w3cwebsocket as W3CWebSocket } from "websocket"
import { Auth, Signer } from "aws-amplify"

let wsClient: any = null

export const client = async () => {
  if (wsClient) return wsClient

  if ((await Auth.currentUserInfo()) === null) return wsClient

  const credentials = await Auth.currentCredentials()

  const accessInfo = {
    access_key: credentials.accessKeyId,
    secret_key: credentials.secretAccessKey,
    session_token: credentials.sessionToken,
  }

  const wssUrl = "wss://YOUR-API-ID.execute-api.REGION.amazonaws.com/dev"

  const signedUrl = Signer.signUrl(wssUrl, accessInfo)

  wsClient = new W3CWebSocket(signedUrl)

  wsClient.onerror = function () {
    console.log("[client]: Connection Error")
  }

  wsClient.onopen = function () {
    console.log("[client]: WebSocket Client Connected")
  }

  wsClient.onclose = function () {
    console.log("[client]: Client Closed")
  }

  wsClient.onmessage = function (e: any) {
    if (typeof e.data === "string") {
      console.log("Received: '" + e.data + "'")
    }
  }

  return wsClient
}

然后使用 AWS Cognito 也需要此权限:

{
  "Action": ["execute-api:Invoke"],
  "Resource": "arn:aws:execute-api:REGION:ACCOUNT-ID-OR-WILDCARD:*/*/$connect",
  "Effect": "Allow"
}

我实现了这个签署 AWS 请求 URL 的 Dart 代码。这对于连接到受 IAM 保护的 WebSocket API 网关特别有用。

https://github.com/MohammedNoureldin/aws-url-signer

我知道不鼓励将链接放在答案中,但是将整个 100 行代码复制到这里是没有意义的。

我的实现的用法如下所示:

String getSignedWebSocketUrl(
    {String apiId,
    String region,
    String stage,
    String accessKey,
    String secretKey,
    String sessionToken})