使用NodeJS为Google Cloud Storage创建签名URL

vshtjzan  于 2023-06-22  发布在  Node.js
关注(0)|答案(5)|浏览(153)

我正在尝试为Google Cloud Storage中的私人存储文件创建签名;这样我就可以发布一个限时链接
它的签名太短了…我哪里做错了?

var crypto = require("crypto");

var ttl = new Date().getTime() + 3600;
var id = 'the_target_file.txt';
var bucketName = 'bucket_name';
var POLICY_JSON = "GET\n" + "\n" + "\n" + ttl + "\n" + '/' + bucketName + '/' + id;

// stringify and encode the policy
var stringPolicy = JSON.stringify(POLICY_JSON);
var base64Policy = Buffer(stringPolicy, "utf-8").toString("base64");

// sign the base64 encoded policy
var privateKey = "MY_PRIVATE_KEY";
var sha256 = crypto.createHmac("sha256", privateKey);
var signature = sha256.update(new Buffer(base64Policy, "utf-8")).digest("base64");

console.log ( signature );
vu8f3i0k

vu8f3i0k1#

现在有一个API/模块用于获取签名的URL。
模块:https://www.npmjs.com/package/@google-cloud/storage
API文档:https://googleapis.dev/nodejs/storage/latest/File.html#getSignedUrl
示例

var storage = require('@google-cloud/storage')();
var myBucket = storage.bucket('my-bucket');

var file = myBucket.file('my-file');

//-
// Generate a URL that allows temporary access to download your file.
//-
var request = require('request');

var config = {
  action: 'read',
  expires: '03-17-2025'  // this could also include time (MM-DD-YYYYTHH:MM:SSZ)
};

file.getSignedUrl(config, function(err, url) {
  if (err) {
    console.error(err);
    return;
  }

  // The file is now available to read from this URL.
  request(url, function(err, resp) {
    // resp.statusCode = 200
  });
});
bvjxkvbb

bvjxkvbb2#

意识到我做错了什么…我在散列策略字符串而不是签名。下面的代码现在给出了正确的输出。

var crypto = require("crypto");
var fs = require("fs");

var expiry = new Date().getTime() + 3600;
var key = 'the_target_file';
var bucketName = 'bucket_name';
var accessId = 'my_access_id';
var stringPolicy = "GET\n" + "\n" + "\n" + expiry + "\n" + '/' + bucketName + '/' + key; 
var privateKey = fs.readFileSync("gcs.pem","utf8");
var signature = encodeURIComponent(crypto.createSign('sha256').update(stringPolicy).sign(privateKey,"base64"));   
var signedUrl = "https://" + bucketName + ".commondatastorage.googleapis.com/" + key +"?GoogleAccessId=" + accessId + "&Expires=" + expiry + "&Signature=" + signature;

console.log(signedUrl);

为了完整性...下面是一个PHP版本,它可以做同样的事情,我用它来检查我的结果

$expiry = time() + 3600;
$key = 'the_target_file';
$bucketName = 'bucket_name';
$accessId = 'my_access_id';
$stringPolicy = "GET\n\n\n".$expiry."\n/".$bucketName."/".$key;
$fp = fopen('gcs.pem', 'r');
$priv_key = fread($fp, 8192);
fclose($fp);
$pkeyid = openssl_get_privatekey($priv_key,"password"); 
if (openssl_sign( $stringPolicy, $signature, $pkeyid, 'sha256' )) {
    $signature = urlencode( base64_encode( $signature ) );    
    echo 'https://'.$bucketName.'.commondatastorage.googleapis.com/'.
              $key.'?GoogleAccessId='.$accessId.'&Expires='.$expiry.'&Signature='.$signature;
}
3b6akqbq

3b6akqbq3#

假设这个问题是签名由谷歌桶后端支持的CDN URL,这里什么对我有效(上面的代码不对我有效)。
选项和签名函数调用:

const signUrlOptions = {
  expires: '' + new Date().getTime() + 3600, // one hour
  keyName: '_SIGNING_KEY_NAME_', // URL signing key name (the one one you created in the CDN backend bucket)
  keyBase64: '_SIGNING_KEY_BASE64_', // the URL signing key base64 content (base64-encoded, 128-bit value, ~24 characters)
  baseUrl: '_CDN_BASE_URL_' // your base CDN URL (can be IP http://123.... when dev env or https://cdn_dns_name or https dns name)
}

const signedUrl = signCdnUrl('demo.png', signedUrlOptions);

签名功能:

import { createHmac } from 'crypto';

const BASE64_REPLACE = { '+': '-', '/': '_', '=': '' };

export function signCdnUrl(fileName, opts) {
  // URL to sign
  const urlToSign = `${opts.baseUrl}/${fileName}?Expires=${opts.expires}&KeyName=${opts.keyName}`;

  // Compute signature
  const keyBuffer = Buffer.from(opts.keyBase64, 'base64');
  let signature = createHmac('sha1', keyBuffer).update(urlToSign).digest('base64');
  signature = signature.replace(/[+/=]/g, c => (<any>BASE64_REPLACE)[c]); // might be a better way

  // Add signature to urlToSign and return signedUrl
  return urlToSign + `&Signature=${signature}`;
}

希望这能帮上忙。不知何故,谷歌云文档没有nodejs示例,文件.getSignedUrl()增加了混淆,因为它似乎与CDN URL签名无关。
注意事项:
注意:可能需要将base64 -> buffer的工作作为opts.keyBuffer移到调用者那里

zbsbpyhn

zbsbpyhn4#

如果nodejs @google-cloud/storage库已经是项目的一部分,那么最好的方法就是使用它。下面的代码是由谷歌存储sdk文档为nodejs链接在这里
npm install @google-cloud/storage

function main(bucketName = 'you_bucket_name', filename = 'your_file_path_without_bucket_name') {
    const {Storage} = require('@google-cloud/storage');

    // Creates a client (Parameters not required if you are already in GCP environment)
    const storage = new Storage({
        projectId: 'your_project_id',
        keyFilename: './json_key_path_for_some_service_account.json'
    });

    async function generateV4ReadSignedUrl() {
        // These options will allow temporary read access to the file
        const options = {
            version: 'v4',
            action: 'read',
            expires: Date.now() + 15 * 60 * 1000, // 15 minutes
        };

        // Get a v4 signed URL for reading the file
        const [url] = await storage
            .bucket(bucketName)
            .file(filename)
            .getSignedUrl(options);

        console.log('Generated GET signed URL:');
        console.log(url);
        console.log('You can use this URL with any user agent, for example:');
        console.log(`curl '${url}'`);
    }

    generateV4ReadSignedUrl().catch(console.error);
    // [END storage_generate_signed_url_v4]
}
main(...process.argv.slice(2));
kd3sttzy

kd3sttzy5#

V4签名URL纯js,无google api库。注意规范请求中的所有新行和路径中的斜线。

const crypto = require('crypto');

const bucket_id = 'blabla-414e5'
const object_name = 'abc/def';

const privateKey = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkifZ6...your private key.. PUWvzK3wRRQDzY5A/ccjEXRiSZgM0/autPZlOsVVlMTG\n3dEtbIpYRz7y+yvBH4HIYA==\n-----END PRIVATE KEY-----\n"

const object_uri = `${bucket_id}.appspot.com/${object_name}`
const request_timestamp  = new Date().toISOString().replaceAll('-', '').replaceAll(':', '').substring(0, 15) + 'Z'
//const request_timestamp = '20230614T150520Z'
const datestamp = request_timestamp.substring(0, 8)  
const service_account = bucket_id + '@appspot.gserviceaccount.com'
const credential_scope = `${datestamp}/auto/storage/goog4_request`
const credential = `${service_account}/${credential_scope}`

const expiration = 3600

const canonical_query = [
'x-goog-algorithm=GOOG4-RSA-SHA256',
`x-goog-credential=${encodeURIComponent(credential)}`,
`x-goog-date=${encodeURIComponent(request_timestamp)}`,
`x-goog-expires=${encodeURIComponent(expiration)}`,
'x-goog-signedheaders=host'].join('&')

const canonical_request =
`PUT
/${object_uri}
${canonical_query}
host:storage.googleapis.com

host
UNSIGNED-PAYLOAD`

const canonical_request_hash = crypto.createHash('sha256').update(canonical_request).digest('hex');

const sign_string = 
`GOOG4-RSA-SHA256
${request_timestamp}
${credential_scope}
${canonical_request_hash}`

const signature = crypto.createSign('sha256').update(sign_string).sign(privateKey, 'hex');

const signedUrl = `https://storage.googleapis.com/${object_uri}?${canonical_query}&x-goog-signature=${signature}`;

console.log(signedUrl)

相关问题