依赖:本文需要了解AWS 架构设计基础知识
如果您想异步处理 API 请求或在应用程序架构中添加队列,那么您来对地方了。
本文介绍如何将 Amazon API Gateway 集成为 SQS(简单队列服务)的代理。
创建IAM Lambda执行角色
附加策略
AWSLambdaVPCAccessExecutionRole
AWSLambdaRole
SecretsManagerReadWrite
AmazonSSMFullAccess
AmazonS3FullAccess
CloudWatchFullAccess
信任关系
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"secretsmanager.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
创建IAM APIGateway SQS角色
附加策略
AWSLambdaVPCAccessExecutionRole
AWSLambdaRole
AWSLambdaSQSExecutionRole
AmazonAPIGatewayPushToCloudWatchLogs
AmazonAPIGatewayAdministrator
SecretsManagerReadWrite
信任关系
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"apigateway.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
创建APIGateway SQS lambda
AWSTemplateFormatVersion: 2010-09-09
Parameters:
Environment:
Type: String
Default: DEV
SubProjectName:
Description: The name of the sub project
Type: String
#TODO:
Default: your-subsystem-name
EnvironmentName:
Type: String
#TODO:
Default: d
CustomerName:
Description: The name of the customer
Type: String
#TODO:
Default: your-company-name
ProjectName:
Description: The name of the project
Type: String
#TODO:
Default: your-project-name
ProjectType:
Type: String
#TODO:
Default: iot
HostedZoneName:
Description: The name of route53 hosted
Type: String
#TODO:
Default: dev.xxxx.cn
Certificate:
Type: String
#TODO:
Default: xxxx-xxxx-xxx-xxx-xxxx
SQSKmsMasterKeyId:
Type: String
#TODO:
Default: xxxx-xxxx-xxx-xxx-xxxx
S3BktName:
Type: String
#TODO:
Default: xxxx-xxxx-d-s3
BaseLayerS3FileName:
Type: String
#TODO:
Default: xxxxxxxxxxxxxx
LambdaSubnetIds:
Description: The subnet ids of the lambda service
Type: List<AWS::EC2::Subnet::Id>
#TODO:
Default: subnet-xxxxxxxxxx,subnet-xxxxxxxxxx
LambdaSecurityGroups:
Description: security groups for lambda
Type: List<AWS::EC2::SecurityGroup::Id>
#TODO:
Default: sg-xxxxxxxxxx
Resources:
#create apigateway
APIGateway:
Type: AWS::ApiGateway::RestApi
Properties:
Description: API Endpoint to receive JSON payloads and queue in SQS
Name: !Sub ${
CustomerName}-${
ProjectName}-${
EnvironmentName}-agw
DisableExecuteApiEndpoint: true
Parameters:
endpointConfigurationTypes: REGIONAL
ignore: documentation
EndpointConfiguration:
Types:
- REGIONAL
Tags:
- Key: ApplName
Value: your-app-name
ProdDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn:
- PostMethod
- CapPostMethod
Properties:
RestApiId: !Ref APIGateway
ProdStage:
Properties:
DeploymentId: !Ref ProdDeployment
RestApiId: !Ref APIGateway
StageName: !Ref EnvironmentName
Type: AWS::ApiGateway::Stage
V1Resource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt APIGateway.RootResourceId
PathPart: v1
RestApiId: !Ref APIGateway
#create api mapping
ApiDomainName:
Type: 'AWS::ApiGateway::DomainName'
Properties:
RegionalCertificateArn: !Sub arn:aws-cn:acm:${
AWS::Region}:${
AWS::AccountId}:certificate/${
Certificate}
DomainName: !Sub ${
ProjectName}${
ProjectType}.${
HostedZoneName}
EndpointConfiguration:
Types:
- REGIONAL
ApiPathMapping:
Type: 'AWS::ApiGateway::BasePathMapping'
Properties:
DomainName: !Ref ApiDomainName
RestApiId: !Ref APIGateway
Stage: !Ref ProdStage
#create api route53 record
Rount53DNSRecord:
Type: AWS::Route53::RecordSet
Properties:
HostedZoneName: !Sub ${
HostedZoneName}.
Comment: ApiGateway subsystem DNS.
Name: !Sub ${
ProjectName}${
ProjectType}.${
HostedZoneName}.
Type: A
AliasTarget:
HostedZoneId: !GetAtt 'ApiDomainName.RegionalHostedZoneId'
DNSName: !GetAtt 'ApiDomainName.RegionalDomainName'
#create lambda base layer
LambdaBaseLayer:
Type: AWS::Lambda::LayerVersion
Properties:
LayerName: !Sub ${
CustomerName}-${
ProjectName}-sqs-layerbase-${
EnvironmentName}-lambda
Description: SQS lambda base layer
Content:
S3Bucket: !Ref S3BktName
S3Key: !Ref BaseLayerS3FileName
CompatibleRuntimes:
- nodejs14.x
LicenseInfo: MIT
#create syb-system
Queue:
Type: AWS::SQS::Queue
Properties:
DelaySeconds: 0
MaximumMessageSize: 262144
MessageRetentionPeriod: 1209600
KmsMasterKeyId: !Ref SQSKmsMasterKeyId
QueueName: !Sub ${
CustomerName}-${
ProjectName}-${
SubProjectName}-${
EnvironmentName}-sqs
ReceiveMessageWaitTimeSeconds: 0
VisibilityTimeout: 30
Tags:
- Key: ApplName
Value: your-app-name
PolicySQS:
Type: AWS::SQS::QueuePolicy
Properties:
PolicyDocument:
Statement:
- Action: SQS:*
Effect: Allow
Principal: '*'
Resource: !GetAtt Queue.Arn
Version: '2012-10-17'
Queues:
- !Ref Queue
PostMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: CUSTOM
# Optional if SignatureAuthorizer is used
AuthorizerId: !Ref SignatureAuthorizer
# AuthorizationType: NONE
HttpMethod: POST
Integration:
Credentials: !Sub arn:aws-cn:iam::${
AWS::AccountId}:role/${
CustomerName}-${
ProjectName}-agw-sqs-${
EnvironmentName}-iamr
IntegrationHttpMethod: POST
IntegrationResponses:
- StatusCode: '200'
PassthroughBehavior: NEVER
RequestParameters:
integration.request.header.Content-Type: "'application/x-www-form-urlencoded'"
RequestTemplates:
application/json: Action=SendMessage&MessageBody=$input.body
Type: AWS
Uri: !Sub arn:aws-cn:apigateway:${
RegionName}:sqs:path/${
AWS::AccountId}/${
CustomerName}-${
ProjectName}-${
SubProjectName}-${
EnvironmentName}-sqs
MethodResponses:
- ResponseModels:
application/json: Empty
StatusCode: '200'
ResourceId: !Ref EnqueueResource
RestApiId: !Ref APIGateway
EnqueueResource:
Properties:
ParentId: !Ref V1Resource
PathPart: !Ref SubProjectName
RestApiId: !Ref APIGateway
Type: AWS::ApiGateway::Resource
#signature(optional)
SignatureAuthorizer:
Type: 'AWS::ApiGateway::Authorizer'
Properties:
AuthorizerCredentials: !Sub arn:aws-cn:iam::${
AWS::AccountId}:role/${
CustomerName}-${
ProjectName}-agw-sqs-${
EnvironmentName}-iamr
AuthorizerResultTtlInSeconds: '300'
AuthorizerUri: !Join
- ''
- - 'arn:aws-cn:apigateway:'
- !Ref 'AWS::Region'
- ':lambda:path/2015-03-31/functions/'
- !GetAtt
- SignatureLambdaFunction
- Arn
- /invocations
Type: REQUEST
IdentitySource: method.request.header.Auth
Name: !Sub ${
CustomerName}-${
ProjectName}-signature-${
EnvironmentName}-authorizer
RestApiId: !Ref APIGateway
SignatureLambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${
CustomerName}-${
ProjectName}-signature-${
EnvironmentName}-lambda
Description: signature lambda
Code:
ZipFile: |
exports.handler = async (event) => {
const defaultAllowAllPolicy = {"principalId": "user","policyDocument": {"Version": "2012-10-17","Statement": [{"Action": "execute-api:Invoke","Effect": "Allow","Resource": "*"}]}};
return defaultAllowAllPolicy;
};
Handler: index.handler
Role: !Sub arn:aws-cn:iam::${
AWS::AccountId}:role/${
CustomerName}-${
ProjectName}-agw-sqs-${
EnvironmentName}-iamr
Runtime: nodejs14.x
Environment:
Variables:
# Signature secrets using the Secrets Manager API
SECRETS_MANAGER_NAME: !Sub ${
CustomerName}-${
ProjectName}-${
EnvironmentName}-secrets-manager
LOG_LEVEL: DEBUG
Tags:
- Key: ApplName
Value: your-app-name
#trigger lambda
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub ${
CustomerName}-${
ProjectName}-${
SubProjectName}-${
EnvironmentName}-lambda
Description: sqs rais lambda
Code:
ZipFile: |
exports.handler = function(event, context) {
console.log('Empty lambda need to be replaced!');
};
Handler: index.handler
Layers:
- Ref: LambdaBaseLayer
Role: !Sub arn:aws-cn:iam::${
AWS::AccountId}:role/${
CustomerName}-${
ProjectName}-agw-sqs-${
EnvironmentName}-iamr
Runtime: nodejs14.x
Timeout: 900
# MemorySize: 512
VpcConfig:
SecurityGroupIds: !Ref LambdaSecurityGroups
SubnetIds: !Ref LambdaSubnetIds
Environment:
Variables:
# database secrets using the Secrets Manager API
SECRETS_MANAGER_NAME: !Join [ '-', [ !Ref CustomerName, !Ref ProjectName, !Ref EnvironmentName, 'secrets-manager' ] ]
LOG_LEVEL: DEBUG
Tags:
- Key: ApplName
Value: your-app-name
LambdaFunctionEventSourceMapping:
Type: AWS::Lambda::EventSourceMapping
Properties:
BatchSize: 10
Enabled: true
EventSourceArn: !GetAtt Queue.Arn
FunctionName: !GetAtt LambdaFunction.Arn
问题:Request Application/xml
一、“集成请求”
1.添加HTTP 标头
Content-Type : 'application/x-www-form-urlencoded'
2.添加映射模板
1)添加application/xml
Action=SendMessage&MessageBody={"body" : $input.json('$')}
2)添加application/json
Action=SendMessage&MessageBody={"body" : $input.json('$')}
二、“集成响应”
1.200 响应的映射模板添加application/xml
#set($inputParam=$input.path('$'))
$inputParam.body
## 这里可以加入你自己的XML格式
三、“方法响应”
1,200响应正文添加application/xml
保存即可
问题:授权方的使用
一、标准的OAuth应用
方法请求中选择授权方,新建Lambda,在Lambda中获取Head ->Authrization,编码验证令牌。
二、参数签名
参数列表+Head[签名]字段,客户端签名,服务器验证签名。 私钥
三、安全控制
WAF控制进站,限制访问的源
四、Body签名
Signator 不支持Body传参【也不知道设计者怎么想的】
如果要Body解析后验证签名,目前可行的方案是:
ApiGateway: 开放接口集成
Signater: 微服务或身份验证(body除外)
Lambda:验证签名,放置队列SQS(不限制启动数量、性能要求高)
SQS:削峰
Lambda:订阅SQS(限制启动数量)
RDS或S3等持久化