使用Indy调用Shopify生成的Google Storage认证URL时遇403错误求助
问题分析与解决
问题描述
从Shopify获取了存储批量操作结果的Google Storage认证URL,在浏览器中粘贴该URL可正常获取jsonl响应,但使用Indy组件发起GET请求时收到403错误,错误信息显示SignatureDoesNotMatch。
认证URL
https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/l9qx25bn40srq20ihwremefeaa81?GoogleAccessId=assets-us-prod%40shopify-tiers.iam.gserviceaccount.com&Expires=1680018264&Signature=g8tFS3W%2FR2O6GfU74XCVttX954dcdycDPvoHklRnuUHAjpNwyxkwNsnK40r6%2FaxRAWL3X8XdWsBmBVLV4o94YiJTwg9yG7RQLIgJyytr3Rmx3ei1nPClRFQuE1Opm%2Fx1nhQXStTGs10BqQ98rqWgNhdG%2BSMqA2KB1QdYA6e1qzfDd%2F1VjSuI21ntQJswgVy0M5cY%2FPKo8%2FR%2F1U8K95V%2FjpXX12t9pgKpdU1axi0N0EpCzcFsgBMna2%2B0pT9qzS9ALV%2FzsE2QcFpcRyCIo1g7jX6apX1ZDlINYSWPzhqhQCZXV4bM0BmCIadIWOkjMT1YiMZecIoctTtzKgytsCXjdA%3D%3D
代码实现
uses IdHTTP, IdSSLOpenSSL; procedure TfrmMain.btnDownloadFromGoogleClick(Sender: TObject); var FileStream : TFileStream; FURL, FResponseFile: string; SSL : TIdSSLIOHandlerSocketOpenSSL; Api : Tidhttp; begin FURL := 'https://storage.googleapis.com/shopify-tiers-assets-prod-us-east1/l9qx25bn40srq20ihwremefeaa81?GoogleAccessId=assets-us-prod%40shopify-tiers.iam.gserviceaccount.com&Expires=1680018264&Signature=g8tFS3W%2FR2O6GfU74XCVttX954dcdycDPvoHklRnuUHAjpNwyxkwNsnK40r6%2FaxRAWL3X8XdWsBmBVLV4o94Y' + 'iJTwg9yG7RQLIgJyytr3Rmx3ei1nPClRFQuE1Opm%2Fx1nhQXStTGs10BqQ98rqWgNhdG%2BSMqA2KB1QdYA6e1qzfDd%2F1VjSuI21ntQJswgVy0M5cY%2FPKo8%2FR%2F1U8K95V%2FjpXX12t9pgKpdU1axi0N0EpCzcFsgBMna2%2B0pT9qzS9ALV%2FzsE2QcFpcRyCIo1g7jX6apX1ZDlINYSWPzhqhQCZXV4bM0BmCIadIWOkjMT1YiMZecIoctTtzKgytsCXjdA%3D%3D'; FResponseFile := 'bulkresponsegraphql.jsonl'; FileStream := TFileStream.create(FResponseFile, fmCreate); try Api := Tidhttp.create(); try SSL := TIdSSLIOHandlerSocketOpenSSL.create(Api); try SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; Api.Request.Clear; Api.IOHandler := SSL; Api.HandleRedirects := True; try { same result with/out headers Api.Request.CustomHeaders.AddValue('GoogleAccessId', 'assets-us-prod%40shopify-tiers.iam.gserviceaccount.com'); Api.Request.CustomHeaders.AddValue('Signature', 'g8tFS3W%2FR2O6GfU74XCVttX954dcdycDPvoHklRnuUHAjpNwyxkwNsnK40r6%2FaxRAWL3X8XdWsBmBVLV4o94YiJTwg9yG7RQLIgJyytr3Rmx3ei1nPClRFQuE1Opm%2Fx1nhQXStTGs10BqQ98rqWgNhdG%2BSMqA2K' + 'B1QdYA6e1qzfDd%2F1VjSuI21ntQJswgVy0M5cY%2FPKo8%2FR%2F1U8K95V%2FjpXX12t9pgKpdU1axi0N0EpCzcFsgBMna2%2B0pT9qzS9ALV%2FzsE2QcFpcRyCIo1g7jX6apX1ZDlINYSWPzhqhQCZXV4bM0BmCIadIWOkjMT1YiMZecIoctTtzKgytsCXjdA%3D%3D'); Api.Request.CustomHeaders.AddValue('Expires', '1680018264'); } Api.Get(FURL, FileStream); except on e: EIdHTTPProtocolException do Memo1.Lines.add('HTTP status code: ' + inttostr(e.ErrorCode) + slinebreak + 'Error message: ' + e.Message + ', ' + e.ErrorMessage); on e: exception do Memo1.Lines.add(e.Message); end; finally SSL.free; end; finally Api.free; end; finally FileStream.free; end; end;
错误响应
HTTP status code: 403 Error message: HTTP/1.0 403 Forbidden, <?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>GET 1680018264 /www.google.com/shopify-tiers-assets-prod-us-east1/l9qx25bn40srq20ihwremefeaa81</StringToSign></Error> HTTP/1.0 403 Forbidden
问题根源
从错误返回的StringToSign字段可以看出,Google Storage计算签名时使用的路径是/www.google.com/shopify-tiers-assets-prod-us-east1/l9qx25bn40srq20ihwremefeaa81,但正确的路径应该是/shopify-tiers-assets-prod-us-east1/l9qx25bn40srq20ihwremefeaa81。这说明Indy组件在处理请求时错误地将主机名www.google.com附加到了请求路径前,导致签名验证失败。
解决方法
- 手动设置Request.Host:在发起请求前,明确设置请求的主机头,确保路径解析正确:
Api.Request.Host := 'storage.googleapis.com'; - 检查URL拼接:将完整URL写成一行,避免拼接时引入意外的空格或换行符,确保URL字符串完全正确。
- 验证URL解析:使用
TIdURI解析URL,确认路径部分无误:var URI: TIdURI; begin URI := TIdURI.Create(FURL); try // 输出路径确认是否正确 ShowMessage(URI.Path); finally URI.Free; end; end; - 调整SSL配置:确保
TIdSSLIOHandlerSocketOpenSSL的配置正确,没有代理或自动修改请求的设置。
内容的提问来源于stack exchange,提问作者Joao
相关产品推荐
相关产品推荐

