You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用SageMaker API时Header签名验证失败(403错误)

Flutter中AWS请求签名不匹配导致SageMaker API返回403问题

我在Flutter中实现了AWS请求签名逻辑,调用SageMaker API时返回403状态码,响应提示计算的请求签名与提供的不匹配。

错误响应信息

{"message":"我们计算的请求签名与您提供的签名不匹配。请检查您的AWS秘密访问密钥和签名方法。有关详细信息,请查阅服务文档。

此请求的规范字符串应为"}

签名实现代码

Map<String, String> signRequest(String param) {
  var method = "POST";
  var uri = endpoint;
  var secretKey = secretKey;
  var accessKey = accessKey;
  var region = awsRegion;
  var service = serviceType;
  var host = baseUrl;

  var date = DateFormat("yyyyMMdd'T'HHmmss'Z'").format(DateTime.now().toUtc());
  var date2 = DateFormat("yyyyMMdd").format(DateTime.now().toUtc());

  var requestBody = utf8.encode(json.encode(param.toLowerCase()));
  var hashedPayloads = sha256.convert(requestBody).toString().toLowerCase();

  var canonicalUri = uri;
  var canonicalQuerystring = "";
  var canonicalHeaders =
      "content-type:application/json\nhost:$host\nx-amz-content-sha256:$hashedPayloads\nx-amz-date:$date\n";

  var signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date";
  var canonicalRequest =
      "$method\n$canonicalUri\n$canonicalQuerystring\n$canonicalHeaders\n$signedHeaders\n$hashedPayloads";

  var credentialScope = "$date2/$region/$service/aws4_request";

  var stringToSign =
      "${ApiConstants.hmacShaTypeString}\n$date\n$credentialScope\n${sha256.convert(utf8.encode(canonicalRequest))}";

  var kSecret = "AWS4$secretKey";
  var kDate = Hmac(sha256, utf8.encode(kSecret))
      .convert(utf8.encode(date2))
      .toString();
  var kRegion = Hmac(sha256, utf8.encode(kDate))
      .convert(utf8.encode(region))
      .toString();
  var kService = Hmac(sha256, utf8.encode(kRegion))
      .convert(utf8.encode(service))
      .toString();
  var kSigning = Hmac(sha256, utf8.encode(kService))
      .convert(utf8.encode("aws4_request"))
      .toString();
  var signature = Hmac(sha256, utf8.encode(kSigning))
      .convert(utf8.encode(stringToSign))
      .toString();

  var authorizationHeader =
      "${ApiConstants.hmacShaTypeString} Credential=$accessKey/$credentialScope, SignedHeaders=$signedHeaders, Signature=$signature";

  Map<String, String> headers = {
    "Content-Type": "application/json",
    "X-Amz-Date": date,
    "X-Amz-Content-Sha256": hashedPayloads,
    "Authorization": authorizationHeader
  };
  return headers;
}

问题排查与修复点

  • 日期一致性问题:代码中两次调用DateTime.now().toUtc()生成日期,极端情况会跨秒导致date和date2所属日期不一致。应先获取一次UTC时间再格式化:
    var utcNow = DateTime.now().toUtc();
    var date = DateFormat("yyyyMMdd'T'HHmmss'Z'").format(utcNow);
    var date2 = DateFormat("yyyyMMdd").format(utcNow);
    
  • 请求体不必要的小写处理:对param调用toLowerCase()会修改请求体内容,若SageMaker API期望原始参数格式,会导致请求体哈希与AWS计算值不匹配。应去掉该处理,直接使用json.encode(param)。
  • Canonical URI错误:canonicalUri应仅包含请求路径,而非完整endpoint URL。例如endpoint为https://runtime.sagemaker.us-east-1.amazonaws.com/endpoints/my-endpoint/invocations时,canonicalUri需设为/endpoints/my-endpoint/invocations。
  • HMAC链式计算错误:中间密钥(kDate、kRegion等)需基于原始字节传递,而非十六进制字符串。之前的.toString()会导致哈希计算错误,修正后代码如下:
    var kSecret = utf8.encode("AWS4$secretKey");
    var kDate = Hmac(sha256, kSecret).convert(utf8.encode(date2)).bytes;
    var kRegion = Hmac(sha256, kDate).convert(utf8.encode(region)).bytes;
    var kService = Hmac(sha256, kRegion).convert(utf8.encode(service)).bytes;
    var kSigning = Hmac(sha256, kService).convert(utf8.encode("aws4_request")).bytes;
    var signature = Hmac(sha256, kSigning).convert(utf8.encode(stringToSign)).toString();
    
  • 签名算法常量验证:确保ApiConstants.hmacShaTypeString的值为AWS4-HMAC-SHA256,这是AWS签名v4的固定要求。

内容的提问来源于stack exchange,提问作者Muhammad Bilal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:40:38