我正在尝试在Flutter应用程序中使用AWS DynamoDB,并且由于缺少用于Dart的官方AWS SDK,我被迫使用低级HTTP REST API。
签署AWS HTTP请求的方法非常繁琐,但是使用AWS supplied sample作为指导,我能够相对容易地将Python转换为Dart。最终结果是两组代码产生相同的auth签名。
当我真正去发送请求时,我的问题出现了。 Python按预期工作,但使用Dart的HTTP包发送POST会产生错误
我们计算的请求签名与您提供的签名不匹配。检查您的AWS Secret Access Key和签名方法。有关详细信息,请参阅服务文档
我将为您提供生成身份验证签名的实际代码,因为只需发送硬编码的相同请求即可复制问题。请参阅下面的Python和Dart代码。
注意:将返回有效的响应
签名已过期:20190307T214900Z现在早于20190307T215809Z(20190307T221309Z - 15分钟)
因为请求签名使用当前日期,并且仅在15分钟内有效。
***** PYTHON代码*****
import requests
headers = {'Content-Type':'application/json',
'X-Amz-Date':'20190307T214900Z',
'X-Amz-Target':'DynamoDB_20120810.GetItem',
'Authorization':'AWS4-HMAC-SHA256 Credential=AKIAJFZWA7QQAQT474EQ/20190307/ap-southeast-2/dynamodb/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-target, Signature=297c5a03c59db6da45bfe2fda6017f89a0a1b2ab6da2bb6e0d838ca40be84320'}
endpoint = 'https://dynamodb.ap-southeast-2.amazonaws.com/'
request_parameters = '{"TableName": "player-exports","Key": {"exportId": {"S": "HG1T"}}}'
r = requests.post(endpoint, data=request_parameters, headers=headers)
print('Response status: %d\n' % r.status_code)
print('Response body: %s\n' % r.text)
***** DART代码*****
import 'package:http/http.dart' as http;
void main(List<String> arguments) async {
var headers = {'Content-Type':'application/json',
'X-Amz-Date':'20190307T214900Z',
'X-Amz-Target':'DynamoDB_20120810.GetItem',
'Authorization':'AWS4-HMAC-SHA256 Credential=AKIAJFZWA7QQAQT474EQ/20190307/ap-southeast-2/dynamodb/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-target, Signature=297c5a03c59db6da45bfe2fda6017f89a0a1b2ab6da2bb6e0d838ca40be84320'};
var endpoint = 'https://dynamodb.ap-southeast-2.amazonaws.com/';
var request_parameters = '{"TableName": "player-exports","Key": {"exportId": {"S": "HG1T"}}}';
http.post(endpoint, body: request_parameters, headers: headers).then((response) {
print("Response status: ${response.statusCode}");
print("Response body: ${response.body}");
});
}
端点,标题和正文实际上是在两组代码之间复制和粘贴的。
Dart HTTP如何工作,我在这里缺少一些细微差别?是否有标题或request_paramaters的map / string / json转换?
我注意到的一件事是在AWS提供的示例中说明了这一点
对于DynamoDB,请求可以包含任何标头,但必须包括“host”,“x-amz-date”,“x-amz-target”,“content-type”和“Authorization”。除了授权标头之外,标头必须包含在canonical_headers和signed_headers值中,如前所述。这里的订单并不重要。 Python注意:“主机”标题由Python“请求”库自动添加。
但
a)当我将'Host':'dynamodb.ap-southeast-2.amazonaws.com'添加到Dart代码中的标题时,我得到相同的结果
和
b)如果我在Python请求返回后查看r.request.headers,我可以看到它自动添加了一些新标题(Content-Length等),但“Host”不是其中之一。
任何想法为什么看似相同的HTTP请求适用于Python请求但不适用于Dart HTTP?
好的,现在解决了。我的问题部分是一个巨大的用户错误。我正在使用一个新的IDE,当我生成我提供的硬编码示例时,我实际上仍在执行上一个文件。愚蠢,愚蠢,愚蠢。
但...
我能够解决导致我首先提出问题的实际问题。我发现如果你在标题中将内容类型设置为“application / json”,则dart HTTP包会自动附加“; charset = utf-8”。由于此值是auth签名的一部分,因此当AWS对标头中的值进行编码以与用户生成的签名进行比较时,它们不匹配。
修复只是为了确保在设置标题内容类型时,确保手动将其设置为“application / json; charset = utf-8”而不是“application / json”。
事实上here之后发现了关于这个“bug”的更多讨论。