为Amazon CloudFron创建签名url

2024-09-29 00:21:33 发布

您现在位置:Python中文网/ 问答频道 /正文

简短版本:如何使用Python通过Amazon CloudFront/S3“按需”生成签名url,以模仿Nginx的X-Accel-Redirect行为(即保护下载)。

我已经安装了一个Django服务器并运行了Nginx前端。我一直被对它的请求所困扰,最近不得不将它作为一个TornadoWSGI应用程序安装,以防止它在FastCGI模式下崩溃。

现在我有一个问题,我的服务器陷入困境(即,它的大部分带宽正在使用)由于太多的媒体请求正在向它提出,我一直在寻找cdn,我相信Amazon CloudFront/S3将是适合我的解决方案。

我一直在使用Nginx的X-Accel-Redirect头来保护文件不被未经授权的下载,但是对于CloudFront/S3,我没有这种能力——不过它们确实提供了签名的url。到目前为止,我还不是Python专家,也绝对不知道如何正确地创建一个签名URL,所以我希望有人能有一个链接,告诉我如何“按需”创建这些URL,或者愿意在这里解释,这将是非常感谢的。

而且,这是正确的解决方案吗?我不太熟悉CDN,有没有更适合这个的CDN?


Tags: django版本服务器urlamazons3nginxcdn
3条回答

正如许多人已经评论过的,initially accepted answer并不适用于Amazon CloudFront事实上,到目前为止Serving Private Content through CloudFront需要使用专用的CloudFront Signed URLs-因此secretmike's answer是正确的,但是在他自己花时间和Added support for generating signed URLs for CloudFront(非常感谢这一点)之后,它就过时了。

boto现在支持一个专用的create_signed_url方法,而以前的二进制依赖M2Crypto最近也被pure-Python RSA implementation替换,请参见Don't use M2Crypto for cloudfront URL signing

越来越常见的是,可以在相关的单元测试中找到一个或多个好的用法示例(请参见test_signed_urls.py),例如test_canned_policy(self)-请参见setUp(self)以获取引用的变量self.pk_idself.pk_str(显然您需要自己的键):

def test_canned_policy(self):
    """
    Generate signed url from the Example Canned Policy in Amazon's
    documentation.
    """
    url = "http://d604721fxaaqy9.cloudfront.net/horizon.jpg?large=yes&license=yes"
    expire_time = 1258237200
    expected_url = "http://example.com/" # replaced for brevity
    signed_url = self.dist.create_signed_url(
        url, self.pk_id, expire_time, private_key_string=self.pk_str)
    # self.assertEqual(expected_url, signed_url)

这个特性现在是already supported in Botocore,它是Boto3, the latest official AWS SDK for Python的底层库。(以下示例需要安装rsa包,但您也可以使用其他rsa包,只需定义自己的“规范化rsa签名者”。)

用法如下:

    from botocore.signers import CloudFrontSigner
    # First you create a cloudfront signer based on a normalized RSA signer::
    import rsa
    def rsa_signer(message):
        private_key = open('private_key.pem', 'r').read()
        return rsa.sign(
            message,
            rsa.PrivateKey.load_pkcs1(private_key.encode('utf8')),
            'SHA-1')  # CloudFront requires SHA-1 hash
    cf_signer = CloudFrontSigner(key_id, rsa_signer)

    # To sign with a canned policy::
    signed_url = cf_signer.generate_presigned_url(
        url, date_less_than=datetime(2015, 12, 1))

    # To sign with a custom policy::
    signed_url = cf_signer.generate_presigned_url(url, policy=my_policy)

免责声明:我是公关的作者

Amazon CloudFront Signed URLs工作方式与Amazon S3签名的url不同。CloudFront使用基于单独的CloudFront密钥对的RSA签名,您必须在Amazon帐户凭据页中设置该密钥对。下面是一些代码,可以使用M2Crypto库在Python中实际生成一个有时间限制的URL:

为CloudFront创建密钥对

我认为唯一的办法就是通过亚马逊的网站。进入你的AWS“账户”页面,点击“安全凭证”链接。单击“密钥对”选项卡,然后单击“创建新密钥对”。这将为您生成新的密钥对并自动下载私钥文件(pk-xxxxxxxxx.pem)。保持密钥文件的安全性和私密性。还要记下亚马逊的“密钥对ID”,因为我们下一步需要它。

用Python生成一些url

从boto版本2.0开始,似乎不支持生成签名的CloudFront url。Python在标准库中不包含RSA加密例程,因此我们将不得不使用一个附加库。我在这个例子中使用了M2Crypto。

对于非流媒体分发,您必须使用完整的cloudfront URL作为资源,但是对于流媒体,我们仅使用视频文件的对象名称。有关生成仅持续5分钟的URL的完整示例,请参见下面的代码。

此代码松散地基于Amazon在CloudFront文档中提供的PHP示例代码。

from M2Crypto import EVP
import base64
import time

def aws_url_base64_encode(msg):
    msg_base64 = base64.b64encode(msg)
    msg_base64 = msg_base64.replace('+', '-')
    msg_base64 = msg_base64.replace('=', '_')
    msg_base64 = msg_base64.replace('/', '~')
    return msg_base64

def sign_string(message, priv_key_string):
    key = EVP.load_key_string(priv_key_string)
    key.reset_context(md='sha1')
    key.sign_init()
    key.sign_update(message)
    signature = key.sign_final()
    return signature

def create_url(url, encoded_signature, key_pair_id, expires):
    signed_url = "%(url)s?Expires=%(expires)s&Signature=%(encoded_signature)s&Key-Pair-Id=%(key_pair_id)s" % {
            'url':url,
            'expires':expires,
            'encoded_signature':encoded_signature,
            'key_pair_id':key_pair_id,
            }
    return signed_url

def get_canned_policy_url(url, priv_key_string, key_pair_id, expires):
    #we manually construct this policy string to ensure formatting matches signature
    canned_policy = '{"Statement":[{"Resource":"%(url)s","Condition":{"DateLessThan":{"AWS:EpochTime":%(expires)s}}}]}' % {'url':url, 'expires':expires}

    #sign the non-encoded policy
    signature = sign_string(canned_policy, priv_key_string)
    #now base64 encode the signature (URL safe as well)
    encoded_signature = aws_url_base64_encode(signature)

    #combine these into a full url
    signed_url = create_url(url, encoded_signature, key_pair_id, expires);

    return signed_url

def encode_query_param(resource):
    enc = resource
    enc = enc.replace('?', '%3F')
    enc = enc.replace('=', '%3D')
    enc = enc.replace('&', '%26')
    return enc


#Set parameters for URL
key_pair_id = "APKAIAZVIO4BQ" #from the AWS accounts CloudFront tab
priv_key_file = "cloudfront-pk.pem" #your private keypair file
# Use the FULL URL for non-streaming:
resource = "http://34254534.cloudfront.net/video.mp4"
#resource = 'video.mp4' #your resource (just object name for streaming videos)
expires = int(time.time()) + 300 #5 min

#Create the signed URL
priv_key_string = open(priv_key_file).read()
signed_url = get_canned_policy_url(resource, priv_key_string, key_pair_id, expires)

print(signed_url)

#Flash player doesn't like query params so encode them if you're using a streaming distribution
#enc_url = encode_query_param(signed_url)
#print(enc_url)

请确保使用TrustedSigners参数设置为持有密钥对的帐户(如果是您自己的帐户,则为“Self”)来设置分发

请参阅Getting started with secure AWS CloudFront streaming with Python以获取有关使用Python设置流的完整示例

相关问题 更多 >