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

向Google Cloud签名URL发送POST请求返回403 Forbidden问题排查

问题分析:GCS签名URL携带x-goog-custom-time时请求返回403错误

我使用Google Cloud Storage API生成签名URL的代码如下:

url, err := s.client.Bucket(s.bucketName).SignedURL(filename, &storage.SignedURLOptions{
        GoogleAccessID: jwtConf.Email,
        PrivateKey:     jwtConf.PrivateKey,
        Expires:        s.ExpirationTime,
        ContentType:    s.ContentType,
        Scheme:         storage.SigningSchemeV4,
        Method:         "POST",
        Headers:        []string{"x-goog-custom-time:2022-12-12T23:20:50Z", "x-goog-resumable:start"},
    })

生成的签名URL包含预期的签名请求头:

https://storage.googleapis.com/####/test_file?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=####%40####%2F20221030%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20221030T122044Z&X-Goog-Expires=3578&X-Goog-Signature=####&X-Goog-SignedHeaders=host%3Bx-goog-custom-time%3Bx-goog-resumable

但发送POST请求启动可恢复上传时,收到403 Forbidden错误,请求命令如下:

curl --location --request POST '<my_signed_url>' \
--header 'content-length: 0' \
--header 'content-type: application/json' \
--header 'x-goog-custom-time: 2022-12-12T23:20:50Z' \
--header 'x-goog-resumable: start'

错误响应内容:

<?xml version='1.0' encoding='UTF-8'?>
<Error><Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your Google secret key and signing method.</Message>
<StringToSign>GOOG4-RSA-SHA256
20221030T122044Z
20221030/auto/storage/goog4_request
ea35f34d2dc65e9345e5a40a4f76ecddbd306cc25a9e19fa97e056cdd2f0363a</StringToSign><CanonicalRequest>POST
/####/test_file
X-Goog-Algorithm=GOOG4-RSA-SHA256&amp;X-Goog-Credential=####;X-Goog-Date=20221030T122044Z&amp;X-Goog-Expires=3578&amp;X-Goog-SignedHeaders=host%3Bx-goog-custom-time%3Bx-goog-resumable
host:storage.googleapis.com
x-goog-custom-time:2022-12-12T23:20:50Z
x-goog-resumable:start

host;x-goog-custom-time;x-goog-resumable
UNSIGNED-PAYLOAD</CanonicalRequest></Error>

关键现象:从签名URL生成逻辑和请求中移除x-goog-custom-time请求头后,POST请求成功执行。

失败原因解析

问题核心在于x-goog-custom-time的属性及GCS V4签名的规则:

  • x-goog-custom-time属于对象元数据头,而非普通请求头。你在SignedURLOptions.Headers中配置它时,签名过程会将其当作请求头进行签名,但在可恢复上传的初始化POST请求中,GCS服务端会自动把这个头解析为对象元数据,而非请求头,导致签名验证环节的请求头匹配逻辑出错。
  • 可恢复上传的初始化请求不支持将x-goog-custom-time作为请求头传递,若要设置该元数据,需通过SignedURLOptions的Metadata字段配置,而非Headers。

修正后的代码示例

url, err := s.client.Bucket(s.bucketName).SignedURL(filename, &storage.SignedURLOptions{
        GoogleAccessID: jwtConf.Email,
        PrivateKey:     jwtConf.PrivateKey,
        Expires:        s.ExpirationTime,
        ContentType:    s.ContentType,
        Scheme:         storage.SigningSchemeV4,
        Method:         "POST",
        Headers:        []string{"x-goog-resumable:start"},
        Metadata:       map[string]string{"x-goog-custom-time": "2022-12-12T23:20:50Z"},
    })

修正后,签名过程会正确处理元数据,请求时也无需在curl中添加x-goog-custom-time头,服务端即可完成签名验证并正确设置对象的自定义时间。

内容的提问来源于stack exchange,提问作者Rotem Linik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 23:40:34