

# 在 API Gateway 中使用 JWT 授权方控制对 HTTP API 的访问
<a name="http-api-jwt-authorizer"></a>

您可以使用 JSON Web 令牌 (JWT) 作为 [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html) 和 [OAuth 2.0](https://oauth.net/2/) 框架的一部分来限制客户端对您的 API 的访问。

如果您为 API 的路由配置 JWT 授权方，API Gateway 将验证客户端通过 API 请求提交的 JWT。API Gateway 根据令牌中的令牌验证和（可选）作用域来允许或拒绝请求。如果为路由配置作用域，则令牌必须至少包含路由的作用域之一。

您可以为 API 的每个路由配置不同的授权方，也可以为多个路由使用同一个授权方。

**注意**  
没有标准的机制可以将 JWT 访问令牌与其他类型的 JWT（如 OpenID Connect ID 令牌）区分开来。除非您需要 ID 令牌进行 API 授权，否则我们建议您将路由配置为需要授权范围。您还可以将 JWT 授权方配置为需要相应的发布者或受众（身份提供商仅在发布 JWT 访问令牌时才使用他们）。

## 使用 JWT 授权方授权 API 请求
<a name="http-api-jwt-authorizer.evaluation"></a>

API Gateway 使用以下常规工作流程向配置为使用 JWT 授权方的路由授权请求。

1. 检查 [https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-prop-authorizer-identitysource](https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-prop-authorizer-identitysource) 是否有令牌。`identitySource` 只能包含令牌或前缀为 `Bearer` 的令牌。

1. 对令牌解码。

1. 使用从发布者的 `jwks_uri` 中提取的公有密钥检查令牌的算法和签名。目前仅支持基于 RSA 的算法。API Gateway 可以将公有密钥缓存两个小时。最佳实践是，在轮换密钥时，留出一段宽限期，在此期间旧密钥和新密钥均有效。

1. 验证声明。API Gateway 评估以下令牌声明：
   +  [https://datatracker.ietf.org/doc/html/rfc7517#section-4.5](https://datatracker.ietf.org/doc/html/rfc7517#section-4.5) – 令牌必须具有与签署令牌的 `jwks_uri` 中的键匹配的标头声明。
   + [https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1) – 必须匹配为授权方配置的 [https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-model-jwtconfiguration](https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-model-jwtconfiguration)。
   + [https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3) 或 `client_id` – 必须匹配为授权方配置的其中一个 [https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-model-jwtconfiguration](https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-authorizers-authorizerid.html#apis-apiid-authorizers-authorizerid-model-jwtconfiguration) 条目。API Gateway 只有在 `aud` 不存在时才验证 `client_id`。当 `aud` 和 `client_id` 同时存在时，API Gateway 会评估 `aud`。
   + [https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4) – 必须在 UTC 中的当前时间之后。
   + [https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5) – 必须在 UTC 中的当前时间之前。
   + [https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6) – 必须在 UTC 中的当前时间之前。
   + [https://datatracker.ietf.org/doc/html/rfc6749#section-3.3](https://datatracker.ietf.org/doc/html/rfc6749#section-3.3) 或 `scp` – 令牌必须至少包含路由的 [https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-routes-routeid.html#apis-apiid-routes-routeid-prop-updaterouteinput-authorizationscopes](https://docs.aws.amazon.com/apigatewayv2/latest/api-reference/apis-apiid-routes-routeid.html#apis-apiid-routes-routeid-prop-updaterouteinput-authorizationscopes) 中的一个作用域。

如果这些步骤中的任何一个步骤失败，API Gateway 都会拒绝 API 请求。

验证 JWT 后，API Gateway 将令牌中的声明传递给 API 路由的集成。后端资源（如 Lambda 函数）可以访问 JWT 声明。例如，如果 JWT 包含了身份声明 `emailID`，则它在 `$event.requestContext.authorizer.jwt.claims.emailID` 中可供 Lambda 集成使用。有关 API Gateway 向 Lambda 集成发送的负载的更多信息，请参阅[针对 API Gateway 中的 HTTP API 创建 AWS Lambda 代理集成](http-api-develop-integrations-lambda.md)。

## 创建 JWT 授权方
<a name="http-api-jwt-authorizer.create"></a>

在创建 JWT 授权方之前，必须向身份提供商注册客户端应用程序。您必须还创建了 HTTP API。有关创建 HTTP API 的示例，请参阅 [创建 HTTP API](http-api-develop.md#http-api-examples)。

### 使用控制台创建 JWT 授权方
<a name="http-api-jwt-authorizer.create.console"></a>

以下步骤说明如何使用控制台创建 JWT 授权方。

**使用控制台创建 JWT 授权方**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 HTTP API。

1. 在主导航窗格中，选择**授权**。

1. 选择**管理授权方**选项卡。

1. 选择**创建**。

1. 对于**授权方类型**，选择 **JWT**。

1. 配置您的 JWT 授权方，并指定用于定义令牌来源的**身份来源**。

1. 选择**创建**。

#### 使用 AWS CLI 创建 JWT 授权方
<a name="http-api-jwt-authorizer.create.cli"></a>

以下 [create-authorizer](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/create-authorizer.html) 命令创建 JWT 授权方。对于 `jwt-configuration`，为您的身份提供商指定 `Audience` 和 `Issuer`。如果您使用 Amazon Cognito 作为身份提供者，则 `IssuerUrl` 为 `https://cognito-idp.us-east-2.amazonaws.com/userPoolID`。

```
aws apigatewayv2 create-authorizer \
    --name authorizer-name \
    --api-id api-id \
    --authorizer-type JWT \
    --identity-source '$request.header.Authorization' \
    --jwt-configuration Audience=audience,Issuer=IssuerUrl
```

##### 使用 AWS CloudFormation 创建 JWT 授权方
<a name="http-api-jwt-cfn.create"></a>

以下 CloudFormation 模板通过将 Amazon Cognito 用作身份提供者的 JWT 授权方创建 HTTP API。

CloudFormation 模板的输出是 Amazon Cognito 托管用户界面的 URL，客户可以在其中注册并登录来接收 JWT。客户端登录后，会使用 URL 中的访问令牌将客户端重定向到您的 HTTP API。要使用访问令牌调用 API，请将 URL 中的 `#` 更改为 `?`，来使用该令牌作为查询字符串参数。

##### 示例 CloudFormation 模板
<a name="http-api-jwt-cfn-example"></a>

```
AWSTemplateFormatVersion: '2010-09-09'
Description: |
  Example HTTP API with a JWT authorizer. This template includes an Amazon Cognito user pool as the issuer for the JWT authorizer 
  and an Amazon Cognito app client as the audience for the authorizer. The outputs include a URL for an Amazon Cognito hosted UI where clients can 
  sign up and sign in to receive a JWT. After a client signs in, the client is redirected to your HTTP API with an access token 
  in the URL. To invoke the API with the access token, change the '#' in the URL to a '?' to use the token as a query string parameter.

Resources:
  MyAPI:
    Type: AWS::ApiGatewayV2::Api
    Properties: 
      Description: Example HTTP API
      Name: api-with-auth
      ProtocolType: HTTP
      Target: !GetAtt MyLambdaFunction.Arn
  DefaultRouteOverrides:
    Type: AWS::ApiGatewayV2::ApiGatewayManagedOverrides
    Properties: 
      ApiId: !Ref MyAPI
      Route: 
        AuthorizationType: JWT
        AuthorizerId: !Ref JWTAuthorizer
  JWTAuthorizer:
    Type: AWS::ApiGatewayV2::Authorizer
    Properties: 
      ApiId: !Ref MyAPI
      AuthorizerType: JWT
      IdentitySource: 
        - '$request.querystring.access_token'
      JwtConfiguration: 
        Audience: 
        - !Ref AppClient
        Issuer: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}
      Name: test-jwt-authorizer
  MyLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Runtime: nodejs18.x
      Role: !GetAtt FunctionExecutionRole.Arn
      Handler: index.handler
      Code:
        ZipFile: |
          exports.handler = async (event) => {
              const response = {
                  statusCode: 200,
                  body: JSON.stringify('Hello from the ' + event.routeKey + ' route!'),
              };
              return response;
          };
  APIInvokeLambdaPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref MyLambdaFunction
      Action: lambda:InvokeFunction
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${MyAPI}/$default/$default
  FunctionExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17		 	 	 '
        Statement:
          - Effect: Allow
            Principal:
              Service:
              - lambda.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: http-api-user-pool
      AutoVerifiedAttributes:
        - email
      Schema:
        - Name: name
          AttributeDataType: String
          Mutable: true
          Required: true
        - Name: email
          AttributeDataType: String
          Mutable: false
          Required: true
  AppClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      AllowedOAuthFlows: 
        - implicit
      AllowedOAuthScopes: 
        - aws.cognito.signin.user.admin
        - email
        - openid
        - profile
      AllowedOAuthFlowsUserPoolClient: true
      ClientName: api-app-client
      CallbackURLs:
        - !Sub https://${MyAPI}.execute-api.${AWS::Region}.amazonaws.com
      ExplicitAuthFlows:
        - ALLOW_USER_PASSWORD_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH
      UserPoolId: !Ref UserPool
      SupportedIdentityProviders:
        - COGNITO 
  HostedUI:
    Type: AWS::Cognito::UserPoolDomain
    Properties: 
      Domain: !Join
        - '-'
        - - !Ref MyAPI
          - !Ref AppClient
      UserPoolId: !Ref UserPool
Outputs:
  SignupURL:
    Value: !Sub https://${HostedUI}.auth.${AWS::Region}.amazoncognito.com/login?client_id=${AppClient}&response_type=token&scope=email+profile&redirect_uri=https://${MyAPI}.execute-api.${AWS::Region}.amazonaws.com
```

## 更新路由来使用 JWT 授权方
<a name="http-api-jwt-authorizer.create.route"></a>

您可以使用控制台、AWS CLI 或 AWS SDK 更新路由来使用 JWT 授权方。

### 使用控制台更新路由来使用 JWT 授权方
<a name="http-api-jwt-authorizer.create.route"></a>

以下步骤说明如何使用控制台更新路由来使用 JWT 授权方。

**使用控制台创建 JWT 授权方**

1. 通过以下网址登录到 Amazon API Gateway 控制台：[https://console.aws.amazon.com/apigateway](https://console.aws.amazon.com/apigateway)。

1. 选择 HTTP API。

1. 在主导航窗格中，选择**授权**。

1. 选择一个方法，然后从下拉菜单中选择您的授权方，然后选择**附加授权方**。

#### 使用 AWS CLI 更新路由来使用 JWT 授权方
<a name="http-api-jwt-authorizer.create.route"></a>

以下 [update-route](https://docs.aws.amazon.com/cli/latest/reference/apigatewayv2/update-route.html) 命令更新路由以使用 JWT 授权方：

```
aws apigatewayv2 update-route \
   --api-id api-id  \
   --route-id route-id  \
   --authorization-type JWT \
   --authorizer-id authorizer-id \
   --authorization-scopes user.email
```