1

我正在尝试使用签名的 URL 作为身份验证,直接通过 javascript 将图像的 base64 数据上传到 Google 存储,这显然是可以做到的。

根据developers.google.com/storage/docs/reference-methods#putobject,只需设置六个标头即可使其正常工作。同样对于标题“授权”,我试图在这里使用最后一个选项:

developer.google.com/storage/docs/reference-headers#authorization

这是“签名”developers.google.com/storage/docs/authentication#service_accounts

我唯一想使用 PHP 的就是获取签名。这是我一直在努力尝试但没有成功的事情。

PHP & JS 页面/代码

<?php

$theDate   = Date(DATE_RFC822);

function signedURL( $filename, $bucket, $method = 'PUT' ) {
    $signature  = "";
    $duration   = 30;
    $emailID    = "980000000000-ytyertyr@developer.gserviceaccount.com";
    $certs      = array();
    $priv_key   = file_get_contents("9999999999999999999999999999-privatekey.p12");

  if (!openssl_pkcs12_read($priv_key, $certs, 'notasecret')) { echo "Unable to parse the p12 file. OpenSSL error: " . openssl_error_string(); exit(); }

    $expires = time() + $duration;
    $to_sign = ( $method . "\n\n\n" . $expires . "\n" . "/" . $bucket . "/" . $filename ); 

    $RSAPrivateKey = openssl_pkey_get_private($certs["pkey"]);

  if (!openssl_sign( $to_sign, $signature, $RSAPrivateKey, 'sha256' ))
  {
    error_log( 'openssl_sign failed!' );
    $signature = 'failed';
  } else {
    $signature =  urlencode( base64_encode( $signature ) );
  }

  return ( 
    'http://storage.googleapis.com/' . $bucket . '/' . $filename . '?GoogleAccessId=' . $emailID . '&Expires=' . $expires . '&Signature=' . $signature
         );
    openssl_free_key($RSAPrivateKey);
} 
?>
<script>
var base64img  = 'data:image/png;base64,AAABAAIAICA....snip...A';
var xhr        = new XMLHttpRequest();
//PUT test - PUT status "(Canceled)" - OPTION status 200 (OK)
xhr.open("PUT", "<?php echo signedURL('test.png', 'mybucket'); ?>");
//xhr.setRequestHeader("Content-type", "image/png");
xhr.setRequestHeader("x-goog-acl", "public-read"); //try to set public read on file
xhr.setRequestHeader("Content-Length", base64img.length); // Chrome throws error (Refused to set unsafe header "Content-Length" )
xhr.send( base64img );
//GET test.txt temp file - working and returning 200 status (signing must be working ?)
/*
xhr.open("GET", "<?php echo signedURL('test.txt', 'mybucket', 'GET'); ?>");
xhr.send();
*/
//
</script>

Cors xml(似乎很好)-我仅在测试时设置了通配符,并且缓存/最大化时间较短

<?xml version="1.0" ?>
<CorsConfig>
    <Cors>
        <Origins>
            <Origin>*</Origin>
        </Origins>
        <Methods>
            <Method>GET</Method>
            <Method>HEAD</Method>
            <Method>OPTIONS</Method>
            <Method>PUT</Method>
        </Methods>
        <ResponseHeaders>
            <ResponseHeader>accept-encoding</ResponseHeader>
            <ResponseHeader>cache-control</ResponseHeader>
            <ResponseHeader>content-length</ResponseHeader>
            <ResponseHeader>content-type</ResponseHeader>
            <ResponseHeader>expect</ResponseHeader>
            <ResponseHeader>if-modified-since</ResponseHeader>
            <ResponseHeader>origin</ResponseHeader>
            <ResponseHeader>range</ResponseHeader>
            <ResponseHeader>referer</ResponseHeader>
            <ResponseHeader>x-goog-acl</ResponseHeader>
            <ResponseHeader>x-goog-api-version</ResponseHeader>
        </ResponseHeaders>
        <MaxAgeSec>900</MaxAgeSec>
    </Cors>
</CorsConfig>

我已经在文件上测试了 GET 方法,现在返回 200 状态(\n\n - 修复)

更新:

与 Chrome 不同,在 Firefox 中查看它确实返回 403。

4

2 回答 2

3

所以以下几行很奇怪,因为将签名的 URL 与 OAuth 和 PUT 与 POST 混为一谈:

# This looks like a PUT to signed URL
xhr.open("PUT", '<?php echo signedURL('imgfile.png','PUT',30,'mybucketname'); ?>', true);
# But multipart requires POST
xhr.setRequestHeader("Content-type", "multipart/form-data; boundary="+boundary);
# And here's a second form of authorization
xhr.setRequestHeader("Authorization", "OAuth <?php echo $signature; ?>");

multipart/form-data上传需要POST动词,适用于 html 表单:Google Cloud Storage : PUT Object vs POST Object to upload file。?.

只要您在 XMLHttpRequest 中发送自定义标头,我建议您使用PUT OAuth 凭据

xhr.open("PUT", "https://storage.googleapis.com/mybucketname/imgfile.png");
xhr.setRequestHeader("Authorization", "OAuth Bearer 1234567abcdefg");
xhr.setRequestHeader("Content-Length", raw_img_bytes.length);
xhr.send(raw_img_bytes);

签名的网址:

xhr.open("PUT", "https://storage.googleapis.com/mybucketname/imgfile.png?" + 
                "GoogleAccessId=1234567890123@developer.gserviceaccount.com&" +
                "Expires=136891473&" +
                "Signature=BClz9e...WvPcwN%2BmWBPqwg...sQI8IQi1493mw%3D");
xhr.setRequestHeader("Content-Length", raw_img_bytes.length);
xhr.send(raw_img_bytres);
于 2013-05-18T22:12:24.683 回答
0

我猜你的 Content-Type 是已知的(例如 Content-Type:video/mp4)?尝试上传扩展名未知的文件。对我来说,PUT 在这种情况下工作,而不是 Content-Type 不为空时......我不明白为什么......

于 2013-05-30T20:53:44.993 回答