没错,建立此程序需要大量API工作。希望他们尽快在AWS控制台中获得它!
更新:我已将此代码提交给boto-从boto v2.1(2011年10月27日发布)开始,这变得容易得多。 对于boto
<2.1,请使用此处的说明。对于boto
2.1或更高版本,请在我的博客上获取更新的说明:http : //www.secretmike.com/2011/10/aws-
cloudfront-secure-streaming.html 一旦boto v2.1被更多发行版打包,我将在此处更新答案。
要完成您想要的操作,您需要执行以下步骤,以下将详细介绍:
- 创建s3存储桶并上传一些对象(您已经完成了此操作)
- 创建一个Cloudfront“原始访问身份”(基本上是一个AWS账户,以允许Cloudfront访问您的s3存储桶)
- 修改对象上的ACL,以便仅允许您的Cloudfront Origin Access Identity读取它们(这可以防止人们绕过Cloudfront并直接进入s3)
- 使用基本URL和需要签名URL的目录创建一个Cloudfront发行版
- 测试您可以从基本的Cloudfront发行版下载对象,但不能从s3或已签名的Cloudfront发行版下载对象
- 创建用于签名URL的密钥对
- 使用Python生成一些URL
- 测试签名的URL是否有效
1-创建存储桶并上传对象
最简单的方法是通过AWS控制台,但是为了完整起见,我将展示如何使用boto。Boto代码如下所示:
import boto#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEYs3 = boto.connect_s3()#bucket name MUST follow dns guidelinesnew_bucket_name = "stream.example.com"bucket = s3.create_bucket(new_bucket_name)object_name = "video.mp4"key = bucket.new_key(object_name)key.set_contents_from_filename(object_name)
2-创建一个Cloudfront“原始访问身份”
目前,只能使用API执行此步骤。Boto代码在这里:
import boto#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEYcf = boto.connect_cloudfront()oai = cf.create_origin_access_identity(comment='New identity for secure videos')#We need the following two values for later steps:print("Origin Access Identity ID: %s" % oai.id)print("Origin Access Identity S3CanonicalUserId: %s" % oai.s3_user_id)3-修改对象上的ACL
现在,我们已经拥有了特殊的S3用户帐户(我们在上面创建的S3CanonicalUserId),我们需要授予它访问s3对象的权限。通过打开对象的(而不是存储桶的!)权限标签,单击“添加更多权限”按钮,然后将上面获得的很长的S3CanonicalUserId粘贴到新的“
Grantee”字段中,我们可以轻松地使用AWS控制台执行此操作。确保您授予新的权限“打开/下载”权限。
您也可以使用以下boto脚本在代码中执行此操作:
import boto#credentials stored in environment AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEYs3 = boto.connect_s3()bucket_name = "stream.example.com"bucket = s3.get_bucket(bucket_name)object_name = "video.mp4"key = bucket.get_key(object_name)#Now add read permission to our new s3 accounts3_canonical_user_id = "<your S3CanonicalUserID from above>"key.add_user_grant("READ", s3_canonical_user_id)4-创建一个Cloudfront发行版
请注意,直到2.0版(在撰写本文时尚未正式发布)之前,boto才完全不支持自定义来源和私有发行版。下面的代码从boto
2.0分支中提取了一些代码,并将它们砍在一起以使其正常运行,但这并不是很漂亮。2.0分支处理起来更加优雅-如果可能的话,一定要使用它!
import botofrom boto.cloudfront.distribution import DistributionConfigfrom boto.cloudfront.exception import CloudFrontServerErrorimport redef get_domain_from_xml(xml): results = re.findall("<DomainName>([^<]+)</DomainName>", xml) return results[0]#custom class to hack this until boto v2.0 is releasedclass HackedStreamingDistributionConfig(DistributionConfig): def __init__(self, connection=None, origin='', enabled=False, caller_reference='', cnames=None, comment='', trusted_signers=None): DistributionConfig.__init__(self, connection=connection, origin=origin, enabled=enabled, caller_reference=caller_reference, cnames=cnames, comment=comment, trusted_signers=trusted_signers) #override the to_xml() function def to_xml(self): s = '<?xml version="1.0" encoding="UTF-8"?>n' s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">n' s += ' <S3Origin>n' s += ' <DNSName>%s</DNSName>n' % self.origin if self.origin_access_identity: val = self.origin_access_identity s += ' <OriginAccessIdentity>origin-access-identity/cloudfront/%s</OriginAccessIdentity>n' % val s += ' </S3Origin>n' s += ' <CallerReference>%s</CallerReference>n' % self.caller_reference for cname in self.cnames: s += ' <CNAME>%s</CNAME>n' % cname if self.comment: s += ' <Comment>%s</Comment>n' % self.comment s += ' <Enabled>' if self.enabled: s += 'true' else: s += 'false' s += '</Enabled>n' if self.trusted_signers: s += '<TrustedSigners>n' for signer in self.trusted_signers: if signer == 'Self': s += ' <Self/>n' else: s += ' <AwsAccountNumber>%s</AwsAccountNumber>n' % signer s += '</TrustedSigners>n' if self.logging: s += '<Logging>n' s += ' <Bucket>%s</Bucket>n' % self.logging.bucket s += ' <Prefix>%s</Prefix>n' % self.logging.prefix s += '</Logging>n' s += '</StreamingDistributionConfig>n' return s def create(self): response = self.connection.make_request('POST', '/%s/%s' % ("2010-11-01", "streaming-distribution"), {'Content-Type' : 'text/xml'}, data=self.to_xml()) body = response.read() if response.status == 201: return body else: raise CloudFrontServerError(response.status, response.reason, body)cf = boto.connect_cloudfront()s3_dns_name = "stream.example.com.s3.amazonaws.com"comment = "example streaming distribution"oai = "<OAI ID from step 2 above like E23KRHS6GDUF5L>"#Create a distribution that does NOT need signed URLShsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)hsd.origin_access_identity = oaibasic_dist = hsd.create()print("Distribution with basic URLs: %s" % get_domain_from_xml(basic_dist))#Create a distribution that DOES need signed URLShsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)hsd.origin_access_identity = oai#Add some required signers (Self means your own account)hsd.trusted_signers = ['Self']signed_dist = hsd.create()print("Distribution with signed URLs: %s" % get_domain_from_xml(signed_dist))5-测试您可以从Cloudfront下载对象,但不能从s3下载对象
您现在应该可以验证:
- stream.example.com.s3.amazonaws.com/video.mp4-应该提供AccessDenied
- signature_distribution.cloudfront.net/video.mp4-应该提供MissingKey(因为未签名URL)
- basic_distribution.cloudfront.net/video.mp4-应该可以正常工作
必须对测试进行调整才能与您的流播放器一起使用,但是基本思想是只有基本的Cloudfront URL应该可以使用。
6-为CloudFront创建密钥对
我认为唯一的方法就是通过亚马逊的网站。进入您的AWS“账户”页面,然后单击“安全凭证”链接。单击“密钥对”选项卡,然后单击“创建新的密钥对”。这将为您生成一个新的密钥对,并自动下载一个私钥文件(pk-
xxxxxxxxx.pem)。确保密钥文件安全和私密。还要记下亚马逊的“密钥对ID”,因为下一步将需要它。
7-在Python中生成一些URL
从boto 2.0版开始,似乎不支持生成签名的CloudFront
URL。Python在标准库中不包含RSA加密例程,因此我们将不得不使用其他库。在此示例中,我使用了M2Crypto。
对于非流媒体分发,您必须使用完整的Cloudfront
URL作为资源,但是对于流媒体,我们仅使用视频文件的对象名称。有关生成仅持续5分钟的URL的完整示例,请参见下面的代码。
此代码大致基于Amazon在CloudFront文档中提供的PHP示例代码。
from M2Crypto import EVPimport base64import timedef aws_url_base64_enpre(msg): msg_base64 = base64.b64enpre(msg) msg_base64 = msg_base64.replace('+', '-') msg_base64 = msg_base64.replace('=', '_') msg_base64 = msg_base64.replace('/', '~') return msg_base64def 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(str(message)) signature = key.sign_final() return signaturedef create_url(url, enpred_signature, key_pair_id, expires): signed_url = "%(url)s?Expires=%(expires)s&Signature=%(enpred_signature)s&Key-Pair-Id=%(key_pair_id)s" % { 'url':url, 'expires':expires, 'enpred_signature':enpred_signature, 'key_pair_id':key_pair_id, } return signed_urldef 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} #now base64 enpre it (must be URL safe) enpred_policy = aws_url_base64_enpre(canned_policy) #sign the non-enpred policy signature = sign_string(canned_policy, priv_key_string) #now base64 enpre the signature (URL safe as well) enpred_signature = aws_url_base64_enpre(signature) #combine these into a full url signed_url = create_url(url, enpred_signature, key_pair_id, expires); return signed_urldef enpre_query_param(resource): enc = resource enc = enc.replace('?', '%3F') enc = enc.replace('=', '%3D') enc = enc.replace('&', '%26') return enc#Set parameters for URLkey_pair_id = "APKAIAZCZRKVIO4BQ" #from the AWS accounts pagepriv_key_file = "cloudfront-pk.pem" #your private keypair fileresource = 'video.mp4' #your resource (just object name for streaming videos)expires = int(time.time()) + 300 #5 min#Create the signed URLpriv_key_string = open(priv_key_file).read()signed_url = get_canned_policy_url(resource, priv_key_string, key_pair_id, expires)#Flash player doesn't like query params so enpre themenc_url = enpre_query_param(signed_url)print(enc_url)8-试用网址
希望您现在应该拥有一个类似于以下内容的有效网址:
video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ
将其放入您的js中,您应该具有类似以下内容的内容(来自Amazon CloudFront文档中的PHP示例):
var so_canned = new SWFObject('http://location.domname.com/~jvngkhow/player.swf','mpl','640','360','9'); so_canned.addParam('allowfullscreen','true'); so_canned.addParam('allowscr-iptaccess','always'); so_canned.addParam('wmode','opaque'); so_canned.addVariable('file','video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQ'); so_canned.addVariable('streamer','rtmp://s3nzpoyjpct.cloudfront.net/cfx/st'); so_canned.write('canned');概要
如您所见,不是很容易!boto v2将有助于大量设置发行版。我将找出是否有可能在其中获得一些URL生成代码来改进这个很棒的库!



