使用PHP使用JWT为Google云端存储签名URL

Joh*_*ung 14 php cloud google-cloud-storage google-api-php-client

我刚刚开始将我的Google云存储代码从API 1.0版升级到2.0版,我遇到了一些麻烦.

在1.0版本中,我使用签名URL,使用.p12文件取得了巨大成功.然而,在新版本中已弃用,我必须使用Firebase/php-jwt,而不是使用JSON文件.

问题是它只是不工作,我收到错误:

<?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>PUT

image/png
1483626991
/myBucket/folder/test.PNG</StringToSign></Error>
Run Code Online (Sandbox Code Playgroud)

这是用于签名的简化代码.

$string = ($method . "\n" .
          $contentMd5 . "\n" .
          $contentType . "\n" .
          $expiration . "\n" .
          $file);

$signedURL = base64_encode(Firebase\JWT\JWT::encode($string,
        file_get_contents($credentialsFilePath)));
Run Code Online (Sandbox Code Playgroud)

收到signedURL后,我使用正确的数据构建一个URL.我从1.0和2.0更改的唯一部分是您对URL进行签名的部分.此外,我已经检查了响应的"StringToSign"字段中的字符串与我正在签名的字符串完全相同.

在1.0版本中,我签署了这样的URL:

$signedURL = base64_encode((new Google_Signer_P12(
        file_get_contents($p12FilePath),
        'notasecret'
      ))->sign($string));
Run Code Online (Sandbox Code Playgroud)

所有这一切让我相信我正在唱出正确的内容,但是以错误的方式使用JWT功能.还有其他人这样做过吗?你是怎么做到的?

万一有趣的是这是我构建的URL(与1.0一起使用):

$returnArr['url'] = "https://{$bucket}.commondatastorage.googleapis.com/"
    . $prefix . '/' . rawurlencode($file)
    . "?GoogleAccessId=" . rawurlencode($serviceEmail)
    . "&Expires={$expiration}"
    . "&Signature=" . rawurlencode($signature);
Run Code Online (Sandbox Code Playgroud)

fic*_*scr 4

查看JWT 库的源代码,我首先想到的第一件事是,我在注释中注意到,您的有效负载应该是数组或对象,而不是字符串......“ JSON Web 令牌”。

* @param object|array  $payload    PHP object or array

public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
Run Code Online (Sandbox Code Playgroud)

其次,看起来你是双base64编码的...base128?:) 的返回值encode应该是连接在一起的三个 Base64 url​​ 字符串,因此您不需要再次执行此操作。

我想尝试一下:

$payload = ['HTTP_Verb'              => $method,
            'Content_MD5'            => $contentMd5,
            'Content_Type'           => $contentType,
            'Expiration'             => $expiration,
            'Canonicalized_Resource' => $file];

$key = file_get_contents($credentialsFilePath);
$signedURL = Firebase\JWT\JWT::encode($payload, $key); //think base64_encode here is redundant.
Run Code Online (Sandbox Code Playgroud)

参考:签名 URL 页面概述。他们在这些文档中肯定没有很好地解释事情。我猜你已经看过 SDK 了?

如果您想采用字符串路线,则需要使用带有 SHA256 的 RSA 签名进行签名...opensssl_sign或者也可能更容易依赖Google 的 PHP SDK

之后...

好的,决定测试一下。看到 Google Cloud 有免费试用版。安装了gsutil,阅读了一堆文档。如果我理解这种 JWT 方法,那该死的。如果有人可以提供有关该主题的文档,请分享。

这段代码的工作原理:

<?php
$method = 'GET';
$expires = '1503532674';
$container = '/example-bucket/cat.jpeg';

$payload = "{$method}\n\n\n{$expires}\n{$container}";

//assume you have this 'json' formatted key too? Otherwise just load the private key file as is.
$key = file_get_contents('~/oas_private_key.json');
$key = json_decode($key, true);
$key = $key['private_key'];

//if sucessful the encypted string is assigned to $signature
openssl_sign($payload, $signature, $key, OPENSSL_ALGO_SHA256);

$signature = urlencode(base64_encode($signature));    

die("https://storage.googleapis.com/{$container}?GoogleAccessId=oastest@foo.iam.gserviceaccount.com&Expires={$expires}&Signature={$signature}");    
Run Code Online (Sandbox Code Playgroud)

终于没有“SignatureDoesNotMatch”错误了!我个人会使用SDK。只需进行一点初始化,您就可以执行如下操作:

$url = $object->signedUrl(new Timestamp(new DateTime('tomorrow')), [
    'method' => 'PUT'
]);
Run Code Online (Sandbox Code Playgroud)

这也将使未来的升级变得更加容易。