AWS HTTP API-Python请求与Dart HTTP中的请求相同但响应不同

BGH*_*BGH 5 python http-post amazon-web-services dart python-requests

我试图在Flutter应用程序中使用AWS DynamoDB,并且由于缺少Dart的官方AWS开发工具包,我不得不使用低级HTTP REST API。

签名AWS HTTP请求的方法非常繁琐,但是使用AWS提供的示例作为指南,我能够相对轻松地将Python转换为Dart。最终结果是两组代码均产生相同的auth签名。

当我实际去发送请求时,我的问题就来了。Python可以正常运行,但是发送带有Dart HTTP包的POST会出错

我们计算出的请求签名与您提供的签名不匹配。检查您的AWS Secret Access密钥和签名方法。有关详细信息,请查阅服务文档。

我将为您保留用于生成auth签名的实际代码,因为可以通过发送相同的硬编码请求来简单地复制该问题。请参见下面的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)
Run Code Online (Sandbox Code Playgroud)

***** DART CODE *****

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}");
  });
}
Run Code Online (Sandbox Code Playgroud)

端点,标头和正文实际上是在两组代码之间复制和粘贴的。

我在这里缺少Dart HTTP工作方式的细微差别吗?头或request_paramaters发生了map / string / json转换吗?

我注意到的一件事是在AWS提供的示例中它指出

对于DynamoDB,请求可以包含任何标头,但必须包含“主机”,“ x-amz-日期”,“ x-amz-target”,“内容类型”和“授权”。如前所述,除授权标头外,标头必须包含在canonical_headers和signed_headers值中。这里的顺序并不重要。Python注意:Python的“ requests”库自动添加了“ host”头。

a)当我在Dart代码的标题中添加'Host':'dy​​namodb.ap-southeast-2.amazonaws.com'时,我得到相同的结果

b)如果在Python请求返回后查看r.request.headers,我可以看到它自动添加了一些新的头文件(Content-Length等),但“ Host”不是其中之一。

有什么想法为什么看似相同的HTTP请求适用于Python请求而不适用于Dart HTTP?

BGH*_*BGH 1

好的,现在已经解决了。我的问题部分是一个巨大的用户错误。我当时使用的是新的 IDE,当我生成我提供的硬编码示例时,我实际上仍在执行之前的文件。愚蠢,愚蠢,愚蠢。

但...

我能够解决导致我首先提出问题的实际问题。我发现,如果您在标头中将内容类型设置为“application/json”,则 dart HTTP 包会自动附加“; charset=utf-8”。由于此值是身份验证签名的一部分,因此当 AWS 对标头中的值进行编码以与用户生成的签名进行比较时,它们不匹配。

修复只是为了确保在设置标头内容类型时,确保手动将其设置为“application/json; charset=utf-8”而不是“application/json”。

事后在这里发现了关于这个“错误”的更多讨论。