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

使用Dart gcloud包生成Cloud Storage签名URL遇签名不匹配错误

生成Cloud Storage签名URL时的SignatureDoesNotMatch问题

问题背景

Dart的gcloud包文档未涵盖生成Cloud Storage资源限时访问签名URL的内容。参考相关示例尝试创建签名URL,但持续收到错误:
The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.

想了解示例中的stringToSign应包含什么内容。已持有具备Object Viewer权限的服务账号,使用生成的key.json文件获取访问密钥,且存储桶启用了统一访问控制(未启用对象级ACL)。

已尝试的无效签名方式

  • signer.sign(utf8.encode('https://storage.googleapis.com/$bucketName/$wordFile'))
  • signer.sign(utf8.encode('https://storage.googleapis.com/$bucketName'))
  • signer.sign(utf8.encode('$bucketName/$wordFile'))

编辑更新:按文档编写的代码

Future<String> getWavSignedUrl(String wordFile) async {
    const expiration = 172800;
    final bucketName = dotenv.env['cloudWavBucket'];
    final canonicalUri = '/$bucketName/$wordFile';
    final dateTimeNow = DateTime.now().toUtc();
    final expirationDateStamp =
        '${dateTimeNow.year}${dateTimeNow.month.toString().padLeft(2, '0')}${dateTimeNow.day.toString().padLeft(2, '0')}';
    final expirationDateTimeStamp =
        '${dateTimeNow.toIso8601String().replaceAll('-', '').replaceAll(':', '').split('.')[0]}Z';
    final clientEmail = accountCredentials.email;
    final credentialScope =
        '$expirationDateStamp%2Fus%2Fstorage%2Fgoog4_request';
    final credential = '$clientEmail%2F$credentialScope';

    Map<String, String> headers = {};
    headers['host'] = '$bucketName.storage.googleapis.com';
    final sortedHeaderKeys = headers.keys.toList()..sort();
    var canonicalHeaders = '';
    var signedHeaders = '';
    for (var key in sortedHeaderKeys) {
      canonicalHeaders += '$key:${headers[key]?.toLowerCase()}\n';
      signedHeaders += '$key;';
    }
    signedHeaders = signedHeaders.substring(
        0, signedHeaders.length - 1); // remove trailing ';'

    Map<String, dynamic> queryParameters = {};
    queryParameters['X-Goog-Algorithm'] = 'GOOG4-RSA-SHA256';
    queryParameters['X-Goog-Credential'] = credential;
    queryParameters['X-Goog-Date'] = expirationDateTimeStamp;
    queryParameters['X-Goog-Expires'] = expiration;
    queryParameters['X-Goog-SignedHeaders'] = signedHeaders;

    var canonicalQueryString = '';
    final sortedQueryKeys = queryParameters.keys.toList()..sort();
    for (var key in sortedQueryKeys) {
      canonicalQueryString += '$key=${queryParameters[key].toString()}&';
    }
    canonicalQueryString = canonicalQueryString.substring(
        0, canonicalQueryString.length - 1); // remove trailing '&'

    String canonicalRequest = [
      'GET',
      canonicalUri,
      canonicalQueryString,
      canonicalHeaders,
      signedHeaders,
      'UNSIGNED-PAYLOAD'
    ].join('\n');

    String canonicalRequestHash = base64.encode(utf8.encode(canonicalRequest));
    final stringToSign = [
      'GOOG4-RSA-SHA256',
      expirationDateTimeStamp,
      credentialScope,
      canonicalRequestHash,
    ].join('\n');

    final pem = accountCredentials.privateKey;
    final rsaKey = keyFromString(pem);
    final signer = RS256Signer(rsaKey);
    final signedRequestBytes = signer.sign(utf8.encode(stringToSign));
    final base64EncodedSignedRequest =
        Uri.encodeQueryComponent(base64.encode(signedRequestBytes));
    const schemeAndHost = 'https://storage.googleapis.com';
    final signedUrl =
        '$schemeAndHost$canonicalUri?$canonicalQueryString&x-goog-signature=$base64EncodedSignedRequest';
    return signedUrl;
  }

仍出现的错误

SignatureDoesNotMatch
访问被拒绝。

我们计算出的请求签名与你提供的签名不匹配。请检查你的Google密钥和签名方法。

内容的提问来源于stack exchange,提问作者Denis Gathondu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:13:16