繁体   English   中英

通过AWS SDK创建签名的S3和Cloudfront URL

[英]Creating signed S3 and Cloudfront URLs via the AWS SDK

是否有人成功使用AWS SDK生成S3存储桶中对象的签名URL,这些URL也可以在CloudFront上运行? 我正在使用JavaScript AWS SDK ,通过S3链接生成签名URL非常简单。 我刚刚创建了一个私有存储桶并使用以下代码生成URL:

var AWS = require('aws-sdk')
  , s3 = new AWS.S3()
  , params = {Bucket: 'my-bucket', Key: 'path/to/key', Expiration: 20}

s3.getSignedUrl('getObject', params, function (err, url) {
  console.log('Signed URL: ' + url)
})

这很好但我也希望向用户公开CloudFront URL,以便他们可以获得使用CDN的更高下载速度。 我设置了一个CloudFront发行版,它修改了存储桶策略以允许访问。 但是,执行此操作后,任何文件都可以通过CloudFront URL访问,而亚马逊似乎忽略了我的链接中的签名。 在阅读了更多内容后,我发现有人生成一个.pem文件来获取使用CloudFront的签名URL,但为什么S3不需要这样做? 似乎getSignedUrl方法只使用AWS Secret Key和AWS Access Key进行签名。 有没有人得到过这样的设置呢?

更新:经过进一步研究后,CloudFront似乎处理的URL签名完全不同于S3 [link] 但是,我仍然不清楚如何使用Javascript创建签名的CloudFront URL。

更新:我将签名功能从下面的示例代码移动到NPM上的aws-cloudfront-sign包中。 这样你就可以只需要这个包并调用getSignedUrl()


经过一些进一步的调查后,我找到了一个解决方案,它是这个答案与我在Boto库中找到的方法之间的一种组合。 确实,S3 URL签名的处理方式与CloudFront URL签名不同。 如果您只需要签署一个S3链接,那么我的初始问题中的示例代码将适用于您。 但是,如果要生成使用CloudFront分配的签名URL,则会更复杂一些。 这是因为AWS SDK当前不支持CloudFront URL签名,因此您必须自己创建签名。 如果您还需要这样做,这里是基本步骤。 我假设您已经安装了S3存储桶:

配置CloudFront

  1. 创建CloudFront分配
  2. 使用以下设置配置原点
    • 来源域名:{your-s3-bucket}
    • 限制存储桶访问:是
    • 授予Bucket读取权限:是,更新存储桶策略
  3. 创建CloudFront密钥对。 应该可以在这里做到这一点

创建签名的CloudFront URL

要想使用签名的CloudFront URL,您只需使用RSA-SHA1签署策略并将其作为查询参数包含在内。 您可以在此处找到有关自定义策略的更多信息,但我在下面的示例代码中包含了一个基本的策略,可以让您启动并运行。 示例代码适用于Node.js,但该过程可以应用于任何语言。

var crypto = require('crypto')
  , fs = require('fs')
  , util = require('util')
  , moment = require('moment')
  , urlParse = require('url')
  , cloudfrontAccessKey = '<your-cloudfront-public-key>'
  , expiration = moment().add('seconds', 30)  // epoch-expiration-time

// Define your policy.
var policy = {
   'Statement': [{
      'Resource': 'http://<your-cloudfront-domain-name>/path/to/object',
      'Condition': {
         'DateLessThan': {'AWS:EpochTime': '<epoch-expiration-time>'},
      }
   }]
}

// Now that you have your policy defined you can sign it like this:
var sign = crypto.createSign('RSA-SHA1')
  , pem = fs.readFileSync('<path-to-cloudfront-private-key>') 
  , key = pem.toString('ascii')

sign.update(JSON.stringify(policy))
var signature = sign.sign(key, 'base64')

// Finally, you build the URL with all of the required query params:
var url = {
  host: '<your-cloudfront-domain-name>',
  protocol: 'http',
  pathname: '<path-to-s3-object>'
}    
var params = {
  'Key-Pair-Id=' + cloudfrontAccessKey,
  'Expires=' + expiration,
  'Signature=' + signature
}
var signedUrl = util.format('%s?%s', urlParse.format(url), params.join('&'))

return signedUrl

为了使我的代码能够使用Jason Sims的代码,我还必须将策略转换为base64并将其添加到最终的signedUrl中,如下所示:

sign.update(JSON.stringify(policy))
var signature = sign.sign(key, 'base64')

var policy_64 = new Buffer(JSON.stringify(policy)).toString('base64'); // ADDED

// Finally, you build the URL with all of the required query params:
var url = {
  host: '<your-cloudfront-domain-name>',
  protocol: 'http',
  pathname: '<path-to-s3-object>'
}    
var params = {
  'Key-Pair-Id=' + cloudfrontAccessKey,
  'Expires=' + expiration,
  'Signature=' + signature,
  'Policy=' + policy_64  // ADDED 
}

AWS包含一些内置的类和结构,以帮助创建CloudFront的签名URL和Cookie。 我利用这些以及Jason Sims的优秀答案来使其以稍微不同的模式工作(这看起来与他创建的NPM包非常相似)。

即,AWS.CloudFront.Signer类型描述,它描述了创建签名URL和Cookie的过程。

export class Signer {
    /**
     * A signer object can be used to generate signed URLs and cookies for granting access to content on restricted CloudFront distributions.
     * 
     * @param {string} keyPairId - The ID of the CloudFront key pair being used.
     * @param {string} privateKey - A private key in RSA format.
     */
    constructor(keyPairId: string, privateKey: string);

    ....
}

或者是带有策略JSON字符串的选项,或者没有带有url和到期时间的策略的选项。

export interface SignerOptionsWithPolicy {
    /**
     * A CloudFront JSON policy. Required unless you pass in a url and an expiry time. 
     */
    policy: string;
}
export interface SignerOptionsWithoutPolicy {
    /**
     * The URL to which the signature will grant access. Required unless you pass in a full policy.
     */
    url: string
    /**
     * A Unix UTC timestamp indicating when the signature should expire. Required unless you pass in a full policy.
     */
    expires: number
}

示例实施:

import aws, { CloudFront } from 'aws-sdk';

export async function getSignedUrl() {

    // https://abc.cloudfront.net/my-resource.jpg
    const url = <cloud front url/resource>;

    // Create signer object - requires a public key id and private key value
    const signer = new CloudFront.Signer(<public-key-id>, <private-key>);

    // Setup expiration time (one hour in the future, in this case)
    const expiration = new Date();
    expiration.setTime(expiration.getTime() + 1000 * 60 * 60);
    const expirationEpoch = expiration.valueOf();

    // Set options (Without policy in this example, but a JSON policy string can be substituted)
    const options = {
        url: url,
        expires: expirationEpoch
    };

    return new Promise((resolve, reject) => {
        // Call getSignedUrl passing in options, to be handled either by callback or synchronously without callback
        signer.getSignedUrl(options, (err, url) => {
            if (err) {
                console.error(err.stack);
                reject(err);
            }
            resolve(url);
        });
    });
}

暂无
暂无

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

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