简体   繁体   English

使用GuzzleHttp遵循重定向到预先签订S3 URL时,遇到了“SignatureDoesNotMatch”

[英]Getting "SignatureDoesNotMatch" when using GuzzleHttp to follow a redirect to a pre-signed S3 URL

I am consuming an API which receives a POST request with some parameters and uses them to generate a file in S3.我正在使用一个 API,它接收带有一些参数的 POST 请求并使用它们在 S3 中生成一个文件。 This API returns a 303 redirect response with Location: set to the signed URL to access the S3 file.此 API 返回一个 303 重定向响应,其中Location:设置为签名 URL 以访问 S3 文件。

This works file when accessing the API via eg Postman however when accessing it via GuzzleHttp (v7.4) it fails with error SignatureDoesNotMatch .当通过例如 Postman 访问 API 时,此文件有效,但是当通过 GuzzleHttp (v7.4) 访问它时,它会失败并显示错误SignatureDoesNotMatch

While debugging I have used code:在调试时我使用了代码:

$client = new Client([
   'allow_redirects' => true,
   'on_stats' => function (TransferStats $stats) {
        var_dump($stats->getEffectiveUri());
    }
]);

$client->post('https://api.example.com', [
   'json' => [ ... ]
]);

Doing this I have confirmed that the URL is correct and copy/pasting the exact url that is accessed actually works.这样做我已经确认 URL 是正确的,并且复制/粘贴实际访问的确切 url 有效。 However using GuzzleHttp it does not work at all.但是使用 GuzzleHttp 它根本不起作用。

The issue turned out to be that when Guzzle follows redirects, it retains most of the original request headers.事实证明,当 Guzzle 遵循重定向时,它保留了大部分原始请求标头。 However the Amazon computed signature also validates the signature against (at least some of) the headers.然而,亚马逊计算的签名也会针对(至少部分)标头验证签名。 The offending header was the Content-Type header which was still sent even though the request no longer had any content.有问题的标头是Content-Type标头,即使请求不再有任何内容,它仍然被发送。

To fix this I created a new Guzzle middleware:为了解决这个问题,我创建了一个新的 Guzzle 中间件:

use Psr\Http\Message\RequestInterface;

class RemoveContentType {

    public function __invoke(callable $handler) {
        return function (RequestInterface $request, array $options) use ($handler) {
            if ($request->getHeaderLine('Content-Type')
                && $request->getBody()->getSize() === 0) {
                return $handler($request->withoutHeader('Content-Type'), $options);
            }

            return $handler($request, $options);
        };
    }
}

this would remove the content type from a request which has an empty body.这将从具有空正文的请求中删除内容类型。

I then modified my code to use this middleware:然后我修改了我的代码以使用这个中间件:

$stack = HandlerStack::create();
$stack->push(new RemoveContentType ());
$client = new Client([
   'handler' => $stack,
   'allow_redirects' => true,
]);

$client->post('https://api.example.com', [
   'json' => [ ... ]
]);

This finally solved my issue.这终于解决了我的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM