Google Cloud签名URL上传遇SignatureDoesNotMatch错误求助
Google Cloud签名URL上传文件遇SignatureDoesNotMatch错误排查
问题背景
无法通过Google Cloud签名URL完成文件上传,始终遇到SignatureDoesNotMatch错误。已尝试以下方案均未解决:
- 切换V2签名替代V4
- 添加/移除Content-Type
- 改用Axios替代Fetch
- Postman测试
- POST+PUT结合
x-goog-resumable:start - 修改存储桶CORS配置
- 延长超时时间
签名URL生成代码
public static String generateV4PutObjectSignedUrl(String objectName) throws StorageException { // String projectId = "my-project-id"; // String bucketName = "my-bucket"; // String objectName = "my-object"; Storage storage = StorageOptions.getDefaultInstance().getService(); // Define Resource BlobInfo blobInfo = BlobInfo.newBuilder(BlobId.of("mybucket", objectName)).build(); // Generate Signed URL Map<String, String> extensionHeaders = new HashMap<>(); extensionHeaders.put("Content-Type", "application/octet-stream"); URL url = storage.signUrl( blobInfo, 15, TimeUnit.HOURS, Storage.SignUrlOption.httpMethod(HttpMethod.PUT), Storage.SignUrlOption.withExtHeaders(extensionHeaders), Storage.SignUrlOption.withV4Signature() ); String urlS = url.toString(); System.out.println("Generated PUT signed URL:"+urlS); return urlS; }
存储桶CORS配置
[{"maxAgeSeconds": 3600000, "method": ["GET", "PUT", "OPTIONS", "POST"], "origin": ["LIVE", "http://localhost:8080", "http://0.0.0.0:8080"], "responseHeader": ["Content-Type", "x-goog-resumable"]}]
尝试的上传方式
Axios方式
const signedUrl = await fetch("$STORAGE_FUN/" + apkFile.name.replace(" ", "_")).then((res) => res.text()); await axios.put(signedUrl, apkFile);
Fetch方式
const put = await fetch(signedUrl, { method: 'PUT', headers: {'Content-Type': 'application/octet-stream'}, body: apkFile })
Curl方式
curl --location --request PUT 'https://storage.googleapis.com/mybucket/no-heroes-here.apk?GoogleAccessId=autodeploy@company.iam.gserviceaccount.com&Expires=1676643070&Signature=ZYU7CjzWRcKaqjOufUZ%2BkOJA%2FsB1vVpWBZAfpeUKIrmQRHJ6ZgXE7bwmqtaIWwBCbZv%2Fa1naYV3xC2OMqhpZ7ene46ZWS4JH%2FIvVn2U5SMBjYG38SrKqICLfj3l4sN%2BR39ijTxPwP4nWCpvMm39CxtCZAC6n5FovsNterZ7NqByXHP7YXgzOXeiBALabDv%2B%2BnclpVtoJ4K6h%2ByFKyQ6odzT0PTDIvpED4ph%2BIrdptwxraFFpLNL8k0ocAsd1zn9U4cxRrTTFCMzzMaXdhXRfVAtkSl5NW7BosdkRmgGtB%2FA1TEifea%2BnfXyXtbRaW7QG5TA9WxrA3VmHNMWz3At29A%3D%3D' \ -H 'Content-Type:' \ --data-binary '@/somepath/original/noheroeshere/no-heroes-here.apk'
错误信息
<?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 20230216T232500Z 20230216/auto/storage/goog4_request 338a1267c21129d5a991a59881cf42c0112f6cfd5cc067c162e0716adc059d5e</StringToSign> <CanonicalRequest>PUT /mybucket/no-heroes-here.apk X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=autodeploy%40company.iam.gserviceaccount.com%2F20230216%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20230216T232500Z&X-Goog-Expires=54000&X-Goog-SignedHeaders=content-type%3Bhost content-type:application/octet-stream host:storage.googleapis.com content-type;host UNSIGNED-PAYLOAD</CanonicalRequest> </Error>
问题解答
1. 代码中是否存在明显错误?
有两处关键不匹配点:
- 生成签名URL时指定了
Content-Type: application/octet-stream作为扩展头,但你尝试的curl请求中设置的是Content-Type:(空值),请求头与签名时的配置不一致,直接触发签名错误。 - 使用Axios上传时,若直接传入
apkFile(File对象),Axios会自动将Content-Type设为文件的实际类型(比如application/vnd.android.package-archive),而非签名时指定的application/octet-stream,同样会导致头信息不匹配。
2. 是否有办法调试签名错误,查看不匹配的具体内容?
错误返回中的<CanonicalRequest>和<StringToSign>就是核心调试依据:
- 按照V4签名规则,自行重新计算请求的
CanonicalRequest和StringToSign,与返回内容对比,即可定位不匹配项(如请求头、方法、路径、参数等)。 - 开启存储桶的访问日志(Cloud Storage > 存储桶 > 日志记录),日志会记录完整的请求信息,包括实际发送的所有头和参数,方便与签名时的设置做对比。
3. 是否可能是服务账号的权限问题导致?
不会。SignatureDoesNotMatch是签名验证失败,与权限无关。若为权限问题,会返回PermissionDenied或AccessDenied错误。
4. 如果服务账号无效,签名URL是否还会生成?
会生成。签名URL的生成是本地用服务账号的私钥计算签名,无需与Google Cloud服务交互。只有当使用签名URL发起请求时,Google才会验证签名的有效性(包括服务账号是否存在、私钥是否匹配等)。
内容的提问来源于stack exchange,提问作者Laurent Meyer
相关产品推荐
相关产品推荐

