AWS API 具有自定义授权方和 CORS 间歇性的网关 200 然后 403 然后 200 ... 奇怪
AWS API Gateway with Custom Authorizer and CORS Intermittent 200 then 403 then 200 ... Strange
我有一个 1 Amazon Api 网关设置和自定义授权(授权基本上只是 returns 允许任何事情)
我启用了 CORS,这是来自 jQuery 网页的 运行。
我有两个方法
- /vehicles(returns汽车列表)
- /预订(returns 预订详情)
我看到的行为是第一个请求正常,我看到它拉 OPTIONS,然后执行 GET 请求. 然后,我点击了另一种方法OPTIONS,然后get returns a 403,但如果我再次启动请求(在同一资源上),我会得到一个 200
我正在使用 Cloudformation,但在使用无服务器框架时我注意到了相同的行为。
下面是我理智的一些屏幕截图,希望其他人已经看到了这种奇怪之处。
下面是我的 Cloudformation YAML 模板的一部分,我边做边学。
HelloAPI:
Type: AWS::Serverless::Api
Properties:
StageName: !Sub ${Environment}
DefinitionBody:
swagger: 2.0
info:
title:
Ref: AWS::StackName
securityDefinitions:
test-authorizer:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: custom
x-amazon-apigateway-authorizer:
type: token
authorizerUri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunc.Arn}/invocations
authorizerResultTtlInSeconds: 5
paths:
/vehicles:
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${VehiclesLambda.Arn}/invocations
responses: {}
security:
- test-authorizer: []
options:
tags:
- "CORS"
summary: "CORS support"
description: "Enable CORS by returning correct headers\n"
consumes:
- "application/json"
produces:
- "application/json"
parameters: []
responses:
"200":
description: "Default response for CORS method"
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
type: "mock"
requestTemplates:
application/json: "{\n \"statusCode\" : 200\n}\n"
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: "{}\n"
/bookings:
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BookingsLambda.Arn}/invocations
responses: {}
security:
- test-authorizer: []
options:
tags:
- "CORS"
summary: "CORS support"
description: "Enable CORS by returning correct headers\n"
consumes:
- "application/json"
produces:
- "application/json"
parameters: []
responses:
"200":
description: "Default response for CORS method"
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
type: "mock"
requestTemplates:
application/json: "{\n \"statusCode\" : 200\n}\n"
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: "{}\n"
这是我的Anything Goes Authorizer:
'use strict';
const generatePolicy = function(principalId, effect, resource) {
const authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
const policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
const statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
};
exports.handler = (event, context, callback) => {
console.log("Hit Authorizer")
console.log(event)
callback(null, generatePolicy('user123', 'Allow', event.methodArn));
};
还有其他人看到过这个,或者知道如何调试它吗?
我把它放在一个测试站点上,只是有人想看看我所看到的。
在自定义授权代码中,第
行
statementOne.Resource = resource;
将您的资源更改为这种格式 "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/"。
在你的情况下,允许所有这些将是:
statementOne.Resource = arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/*/
这就是 AWS 了解您的授权方的方式。因为在自定义授权方中,您可以从请求 header 中获取用户、组等信息,然后根据您的授权数据库验证信息并决定允许谁或什么继续请求类型 POST/GET/OPTION,但是API 在您以 AWS 格式提供有效答案之前,网关不会知道您的决定
{
"principalId": "yyyyyyyy", // The principal user identification associated with the token sent by the client.
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[child-resources]]"
}
]
},
"context": {
"stringKey": "value",
"numberKey": "1",
"booleanKey": "true"
},
"usageIdentifierKey": "{api-key}" # Optional
}
您可以访问此页面以了解更多信息:
刚刚与 AWS 代表讨论了类似问题。当前的问题是 lambda 授权方缓存,它不同于 API 网关缓存。
您可能默认缓存了 lambda 授权方(见图),因此当您最初发出请求时,您的策略(特定于单个资源)会针对 TTL 进行缓存。对使用相同 LAMBDA AUTHORIZER 的不同资源的后续请求将 return 原始资源的相同策略,这不是手头的资源,因此,您会得到 403。
将 returned 策略更改为更通用的内容,如 @Dominic Nguyen 的回答是一种解决方案(通常涉及添加 /*s),但您也可以按照我的做法,只禁用缓存lambda 授权方:
然后,记得重新部署!!!代表告诉我在那之后等待 30 秒,然后再测试。
我有一个 1 Amazon Api 网关设置和自定义授权(授权基本上只是 returns 允许任何事情)
我启用了 CORS,这是来自 jQuery 网页的 运行。
我有两个方法
- /vehicles(returns汽车列表)
- /预订(returns 预订详情)
我看到的行为是第一个请求正常,我看到它拉 OPTIONS,然后执行 GET 请求. 然后,我点击了另一种方法OPTIONS,然后get returns a 403,但如果我再次启动请求(在同一资源上),我会得到一个 200
我正在使用 Cloudformation,但在使用无服务器框架时我注意到了相同的行为。
下面是我理智的一些屏幕截图,希望其他人已经看到了这种奇怪之处。
下面是我的 Cloudformation YAML 模板的一部分,我边做边学。
HelloAPI:
Type: AWS::Serverless::Api
Properties:
StageName: !Sub ${Environment}
DefinitionBody:
swagger: 2.0
info:
title:
Ref: AWS::StackName
securityDefinitions:
test-authorizer:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: custom
x-amazon-apigateway-authorizer:
type: token
authorizerUri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunc.Arn}/invocations
authorizerResultTtlInSeconds: 5
paths:
/vehicles:
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${VehiclesLambda.Arn}/invocations
responses: {}
security:
- test-authorizer: []
options:
tags:
- "CORS"
summary: "CORS support"
description: "Enable CORS by returning correct headers\n"
consumes:
- "application/json"
produces:
- "application/json"
parameters: []
responses:
"200":
description: "Default response for CORS method"
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
type: "mock"
requestTemplates:
application/json: "{\n \"statusCode\" : 200\n}\n"
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: "{}\n"
/bookings:
get:
x-amazon-apigateway-integration:
httpMethod: POST
type: aws_proxy
uri:
!Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BookingsLambda.Arn}/invocations
responses: {}
security:
- test-authorizer: []
options:
tags:
- "CORS"
summary: "CORS support"
description: "Enable CORS by returning correct headers\n"
consumes:
- "application/json"
produces:
- "application/json"
parameters: []
responses:
"200":
description: "Default response for CORS method"
headers:
Access-Control-Allow-Headers:
type: "string"
Access-Control-Allow-Methods:
type: "string"
Access-Control-Allow-Origin:
type: "string"
x-amazon-apigateway-integration:
type: "mock"
requestTemplates:
application/json: "{\n \"statusCode\" : 200\n}\n"
responses:
default:
statusCode: "200"
responseParameters:
method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
method.response.header.Access-Control-Allow-Methods: "'*'"
method.response.header.Access-Control-Allow-Origin: "'*'"
responseTemplates:
application/json: "{}\n"
这是我的Anything Goes Authorizer:
'use strict';
const generatePolicy = function(principalId, effect, resource) {
const authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
const policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
const statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
return authResponse;
};
exports.handler = (event, context, callback) => {
console.log("Hit Authorizer")
console.log(event)
callback(null, generatePolicy('user123', 'Allow', event.methodArn));
};
还有其他人看到过这个,或者知道如何调试它吗?
我把它放在一个测试站点上,只是有人想看看我所看到的。
在自定义授权代码中,第
行statementOne.Resource = resource;
将您的资源更改为这种格式 "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/"。
在你的情况下,允许所有这些将是:
statementOne.Resource = arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/*/
这就是 AWS 了解您的授权方的方式。因为在自定义授权方中,您可以从请求 header 中获取用户、组等信息,然后根据您的授权数据库验证信息并决定允许谁或什么继续请求类型 POST/GET/OPTION,但是API 在您以 AWS 格式提供有效答案之前,网关不会知道您的决定
{
"principalId": "yyyyyyyy", // The principal user identification associated with the token sent by the client.
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[child-resources]]"
}
]
},
"context": {
"stringKey": "value",
"numberKey": "1",
"booleanKey": "true"
},
"usageIdentifierKey": "{api-key}" # Optional
}
您可以访问此页面以了解更多信息:
刚刚与 AWS 代表讨论了类似问题。当前的问题是 lambda 授权方缓存,它不同于 API 网关缓存。
您可能默认缓存了 lambda 授权方(见图),因此当您最初发出请求时,您的策略(特定于单个资源)会针对 TTL 进行缓存。对使用相同 LAMBDA AUTHORIZER 的不同资源的后续请求将 return 原始资源的相同策略,这不是手头的资源,因此,您会得到 403。
将 returned 策略更改为更通用的内容,如 @Dominic Nguyen 的回答是一种解决方案(通常涉及添加 /*s),但您也可以按照我的做法,只禁用缓存lambda 授权方: