【问题标题】:Creating signed URLs for Google Cloud Storage using NodeJS使用 NodeJS 为 Google Cloud Storage 创建签名 URL
【发布时间】:2014-01-12 07:20:31
【问题描述】:

我正在尝试为 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 );

【问题讨论】:

    标签: node.js google-cloud-storage sha


    【解决方案1】:

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

    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;
    }
    

    【讨论】:

    • 如何减少过期时间。
    • 伙计,这就是我仍然来 SO 的原因。 ⭐️
    • var accessId = 'my_access_id'; 是什么?
    • 很好的例子!一个小提示 - 在 php 中 3600 是秒,而在 js 中 3600 是在 ms,因为 javascript 的 getTime() 返回毫秒。
    • 我注意到这里没有secretAccessKey,我以为这里不再需要了?
    【解决方案2】:

    现在有一个用于获取签名 URL 的 API/模块。

    模块: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
      });
    });
    

    【讨论】:

    • 这似乎只为bucket访问创建链接,而不是为CDN创建链接。为 CDN 添加 cname 选项仍然不会授予服务帐户权限。
    • 这看起来像是对先前复杂需求的一个非常方便的抽象。在这种情况下,使用什么来签署 URL?
    • 值得注意的是,您也可以以 MM-DD-YYYYTHH:MM:SSZ 格式为到期时间计时 - 小数秒似乎会导致此问题,因此请确保您的字符串不包含“Z”之前的小数和/或小数部分
    • 真的是 MM-DD-YYYY 而不是像 ISO 日期那样的 YYYY-MM-DD 正常吗?
    • @max 是的,仍然正确:googleapis.dev/nodejs/storage/latest/… 但您也可以使用时间戳A timestamp when this link will expire. Any value given is passed to new Date(). Note: 'v4' supports maximum duration of 7 days (604800 seconds) from now.
    【解决方案3】:

    假设这个问题是签署由谷歌存储桶后端支持的 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 示例,并且 file.getSignedUrl() 增加了混乱,因为它似乎与 CDN URL 签名无关。

    注意:

    注意:可能希望将 base64 -> 缓冲区工作作为 opts.keyBuffer 转移给调用者

    【讨论】:

      【解决方案4】:

      如果 nodejs @google-cloud/storage 库已经是您项目的一部分,那么最好的方法是使用它。下面的代码来自 google storage sdk docs for nodejs Link here

      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));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 2015-08-30
        • 1970-01-01
        • 1970-01-01
        • 2017-02-25
        • 2017-09-16
        相关资源
        最近更新 更多