要完成您想要的操作,您需要执行以下步骤,我将在下面详细说明:创建您的 s3 存储桶并上传一些对象(您已完成此操作)创建 Cloudfront源访问身份"(基本上是一个 AWS 帐户,允许 cloudfront 访问您的 s3 存储桶)修改对象上的 ACL,以便仅允许您的 Cloudfront Origin Access Identity 读取它们(这可以防止人们绕过 Cloudfront 并直接进入 s3)创建一个包含基本 URL 和需要签名 URL 的 Cloudfront 分发测试您是否可以从基本的 Cloudfront 发行版下载对象,但不能从 s3 或签名的 Cloudfront 发行版下载对象创建用于签署 URL 的密钥对使用 Python 生成一些 URL测试签名网址是否有效1 - 创建 Bucket 并上传对象最简单的方法是通过 AWS 控制台,但为了完整起见,我将展示如何使用 boto.Boto 代码显示在此处:导入boto#credentials 存储在环境 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY 中s3 = boto.connect_s3()#bucket 名称必须遵循 dns 指南new_bucket_name = "stream.example.com"存储桶 = 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 代码在这里:导入boto#credentials 存储在环境 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY 中cf = boto.connect_cloudfront()oai = cf.create_origin_access_identity(comment='安全视频的新身份')#我们需要以下两个值用于后面的步骤:打印(源访问身份ID:%s"% oai.id)打印(原始访问身份 S3CanonicalUserId:%s"% oai.s3_user_id)3 - 修改对象上的 ACL现在我们已经有了我们的特殊 S3 用户帐户(我们在上面创建的 S3CanonicalUserId),我们需要让它访问我们的 s3 对象.我们可以使用 AWS 控制台轻松完成此操作,方法是打开对象的(不是存储桶的!)权限选项卡,单击添加更多权限"按钮,然后将我们在上面获得的很长的 S3CanonicalUserId 粘贴到新的被授予者"字段中.确保您授予新权限打开/下载"权限.您也可以使用以下 boto 脚本在代码中执行此操作:导入boto#credentials 存储在环境 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY 中s3 = boto.connect_s3()bucket_name = "stream.example.com"桶 = s3.get_bucket(bucket_name)object_name = "video.mp4"key = bucket.get_key(object_name)#现在为我们的新 s3 帐户添加读取权限s3_canonical_user_id = ""key.add_user_grant("READ", s3_canonical_user_id)4 - 创建云前端分发请注意,在撰写本文时尚未正式发布的 2.0 版之前,boto 并不完全支持自定义来源和私有分发.下面的代码从 boto 2.0 分支中提取了一些代码并将其组合在一起以使其运行,但它并不漂亮.2.0 分支更优雅地处理了这一点 - 如果可能,一定要使用它!导入boto从 boto.cloudfront.distribution 导入 DistributionConfig从 boto.cloudfront.exception 导入 CloudFrontServerError进口重新def get_domain_from_xml(xml):结果 = re.findall("([^", xml)返回结果[0]#custom 类来破解这个直到 boto v2.0 发布类 HackedStreamingDistributionConfig(DistributionConfig):def __init__(self, connection=None, origin='', enabled=False,caller_reference='', cnames=None, comment='',可信签名者=无):DistributionConfig.__init__(self, connection=connection,原点=原点,启用=启用,caller_reference=caller_reference,cnames=cnames,comment=comment,受信任的签名者=受信任的签名者)#覆盖 to_xml() 函数def to_xml(self):s = '<?xml version="1.0" encoding="UTF-8"?>'s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">'s += ' 's += ' <DNSName>%s</DNSName>' % self.origin如果 self.origin_access_identity:val = self.origin_access_identitys += ' origin-access-identity/cloudfront/%s' % vals += ' </S3Origin>'s += ' <CallerReference>%s</CallerReference>' % self.caller_reference对于 self.cnames 中的 cname:s += ' %s' % cname如果 self.comment:s += ' <Comment>%s</Comment>' % self.comments += ' '如果 self.enabled:s += '真'别的:s += '假's += ''如果 self.trusted_signers:s += ''对于 self.trusted_signers 中的签名者:如果签名者 == '自我':s += ' <自己/>'别的:s += ' %s' % 签名者s += '</TrustedSigners>'如果 self.logging:s += ''s += ' <Bucket>%s</Bucket>' % self.logging.buckets += ' <Prefix>%s</Prefix>' % self.logging.prefixs += '</日志记录>'s += ''返回定义创建(自我):response = self.connection.make_request('POST','/%s/%s' % ("2010-11-01", "streaming-distribution"),{'内容类型':'文本/xml'},数据=self.to_xml())正文 = response.read()如果 response.status == 201:返回身体别的:引发 CloudFrontServerError(response.status, response.reason, body)cf = boto.connect_cloudfront()s3_dns_name = "stream.example.com.s3.amazonaws.com"评论 =示例流分发"oai = ""#创建一个不需要签名 URL 的发行版hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)hsd.origin_access_identity = oaibasic_dist = hsd.create()打印(带有基本 URL 的分布:%s" % get_domain_from_xml(basic_dist))#创建一个不需要签名 URL 的发行版hsd = HackedStreamingDistributionConfig(connection=cf, origin=s3_dns_name, comment=comment, enabled=True)hsd.origin_access_identity = oai#添加一些需要的签名者(Self是指自己的账号)hsd.trusted_signers = ['Self']signed_dist = hsd.create()print("带有签名 URL 的分发:%s" % get_domain_from_xml(signed_dist))5 - 测试您可以从 cloudfront 下载对象,但不能从 s3 下载对象您现在应该能够验证:stream.example.com.s3.amazonaws.com/video.mp4 - 应该给 AccessDeniedsigned_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 EVP导入 base64导入时间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('/', '~')返回 msg_base64def sign_string(消息,priv_key_string):key = EVP.load_key_string(priv_key_string)key.reset_context(md='sha1')key.sign_init()key.sign_update(str(message))签名 = key.sign_final()返回签名def create_url(url, encoding_signature, key_pair_id, expires):signed_url = "%(url)s?Expires=%(expires)s&Signature=%(encoded_signature)s&Key-Pair-Id=%(key_pair_id)s" % {网址":网址,'过期':过期,'encoded_signature':encoded_signature,'key_pair_id':key_pair_id,}返回signed_urldef get_canned_policy_url(url, priv_key_string, key_pair_id, expires):#我们手动构建此策略字符串以确保格式匹配签名canned_policy = '{"Statement":[{"Resource":"%(url)s","Condition":{"DateLessThan":{"AWS:EpochTime":%(expires)s}}}]}' %{'url':url, 'expires':expires}#now base64 对其进行编码(必须是 URL 安全的)编码策略 = aws_url_base64_encode(canned_policy)#签署非编码策略签名 = sign_string(canned_policy, priv_key_string)#now base64 对签名进行编码(URL 也是安全的)编码签名 = aws_url_base64_encode(签名)#将这些组合成一个完整的网址signed_url = create_url(url,encoded_signature,key_pair_id, expires);返回signed_urldef encode_query_param(资源):enc = 资源enc = enc.replace('?', '%3F')enc = enc.replace('=', '%3D')enc = enc.replace('&', '%26')返回编码#设置URL参数key_pair_id = "APKAIAZCZRKVIO4BQ" #来自 AWS 账户页面priv_key_file = "cloudfront-pk.pem" #你的私钥对文件resource = 'video.mp4' #你的资源(只是流视频的对象名称)expires = int(time.time()) + 300 #5 分钟#创建签名的URLpriv_key_string = 打开(priv_key_file).read()signed_url = get_canned_policy_url(资源,priv_key_string,key_pair_id,过期)#Flash 播放器不喜欢查询参数,因此对其进行编码enc_url = encode_query_param(signed_url)打印(enc_url)8 - 试用网址希望您现在应该有一个如下所示的有效网址: video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x〜KOudJky7anTuX0oAgL〜1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd〜CjYdxZBWpxTsPO3yIFNJI〜R2AFpWx8qp3fs38Yw_%26Key线对-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('allowscriptaccess','always');so_canned.addParam('wmode','opaque');so_canned.addVariable( '文件', 'video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x〜KOudJky7anTuX0oAgL〜1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd〜CjYdxZBWpxTsPO3yIFNJI〜R2AFpWx8qp3fs38Yw_%26Key线对-ID%3DAPKAIAZRKVIO4BQ');so_canned.addVariable('streamer','rtmp://s3nzpoyjpct.cloudfront.net/cfx/st');so_canned.write('canned');总结如您所见,这并不容易!boto v2 对设置发行版有很大帮助.我会找出是否有可能在那里获得一些 URL 生成代码来改进这个伟大的库!I have created a S3 bucket, uploaded a video, created a streaming distribution in CloudFront. Tested it with a static HTML player and it works. I have created a keypair through the account settings. I have the private key file sitting on my desktop at the moment. That's where I am.My aim is to get to a point where my Django/Python site creates secure URLs and people can't access the videos unless they've come from one of my pages. The problem is I'm allergic to the way Amazon have laid things out and I'm just getting more and more confused.I realise this isn't going to be the best question on StackOverflow but I'm certain I can't be the only fool out here that can't make heads or tails out of how to set up a secure CloudFront/S3 situation. I would really appreciate your help and am willing (once two days has passed) give a 500pt bounty to the best answer.I have several questions that, once answered, should fit into one explanation of how to accomplish what I'm after:In the documentation (there's an example in the next point) there's lots of XML lying around telling me I need to POST things to various places. Is there an online console for doing this? Or do I literally have to force this up via cURL (et al)?How do I create a Origin Access Identity for CloudFront and bind it to my distribution? I've read this document but, per the first point, don't know what to do with it. How does my keypair fit into this?Once that's done, how do I limit the S3 bucket to only allow people to download things through that identity? If this is another XML jobby rather than clicking around the web UI, please tell me where and how I'm supposed to get this into my account.In Python, what's the easiest way of generating an expiring URL for a file. I have boto installed but I don't see how to get a file from a streaming distribution.Are there are any applications or scripts that can take the difficulty of setting this garb up? I use Ubuntu (Linux) but I have XP in a VM if it's Windows-only. I've already looked at CloudBerry S3 Explorer Pro - but it makes about as much sense as the online UI. 解决方案 You're right, it takes a lot of API work to get this set up. I hope they get it in the AWS Console soon!UPDATE: I have submitted this code to boto - as of boto v2.1 (released 2011-10-27) this gets much easier. For boto < 2.1, use the instructions here. For boto 2.1 or greater, get the updated instructions on my blog: http://www.secretmike.com/2011/10/aws-cloudfront-secure-streaming.html Once boto v2.1 gets packaged by more distros I'll update the answer here.To accomplish what you want you need to perform the following steps which I will detail below:Create your s3 bucket and upload some objects (you've already done this)Create a Cloudfront "Origin Access Identity" (basically an AWS account to allow cloudfront to access your s3 bucket)Modify the ACLs on your objects so that only your Cloudfront Origin Access Identity is allowed to read them (this prevents people from bypassing Cloudfront and going direct to s3)Create a cloudfront distribution with basic URLs and one which requires signed URLsTest that you can download objects from basic cloudfront distribution but not from s3 or the signed cloudfront distributionCreate a key pair for signing URLsGenerate some URLs using PythonTest that the signed URLs work1 - Create Bucket and upload objectThe easiest way to do this is through the AWS Console but for completeness I'll show how using boto. Boto code is shown here: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 - Create a Cloudfront "Origin Access Identity"For now, this step can only be performed using the API. Boto code is here: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 - Modify the ACLs on your objectsNow that we've got our special S3 user account (the S3CanonicalUserId we created above) we need to give it access to our s3 objects. We can do this easily using the AWS Console by opening the object's (not the bucket's!) Permissions tab, click the "Add more permissions" button, and pasting the very long S3CanonicalUserId we got above into the "Grantee" field of a new. Make sure you give the new permission "Open/Download" rights.You can also do this in code using the following boto script: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 - Create a cloudfront distributionNote that custom origins and private distributions are not fully supported in boto until version 2.0 which has not been formally released at time of writing. The code below pulls out some code from the boto 2.0 branch and hacks it together to get it going but it's not pretty. The 2.0 branch handles this much more elegantly - definitely use that if possible!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"?>' s += '<StreamingDistributionConfig xmlns="http://cloudfront.amazonaws.com/doc/2010-07-15/">' s += ' <S3Origin>' s += ' <DNSName>%s</DNSName>' % self.origin if self.origin_access_identity: val = self.origin_access_identity s += ' <OriginAccessIdentity>origin-access-identity/cloudfront/%s</OriginAccessIdentity>' % val s += ' </S3Origin>' s += ' <CallerReference>%s</CallerReference>' % self.caller_reference for cname in self.cnames: s += ' <CNAME>%s</CNAME>' % cname if self.comment: s += ' <Comment>%s</Comment>' % self.comment s += ' <Enabled>' if self.enabled: s += 'true' else: s += 'false' s += '</Enabled>' if self.trusted_signers: s += '<TrustedSigners>' for signer in self.trusted_signers: if signer == 'Self': s += ' <Self/>' else: s += ' <AwsAccountNumber>%s</AwsAccountNumber>' % signer s += '</TrustedSigners>' if self.logging: s += '<Logging>' s += ' <Bucket>%s</Bucket>' % self.logging.bucket s += ' <Prefix>%s</Prefix>' % self.logging.prefix s += '</Logging>' s += '</StreamingDistributionConfig>' 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 - Test that you can download objects from cloudfront but not from s3You should now be able to verify:stream.example.com.s3.amazonaws.com/video.mp4 - should give AccessDeniedsigned_distribution.cloudfront.net/video.mp4 - should give MissingKey (because the URL is not signed)basic_distribution.cloudfront.net/video.mp4 - should work fineThe tests will have to be adjusted to work with your stream player, but the basic idea is that only the basic cloudfront url should work.6 - Create a keypair for CloudFrontI think the only way to do this is through Amazon's web site. Go into your AWS "Account" page and click on the "Security Credentials" link. Click on the "Key Pairs" tab then click "Create a New Key Pair". This will generate a new key pair for you and automatically download a private key file (pk-xxxxxxxxx.pem). Keep the key file safe and private. Also note down the "Key Pair ID" from amazon as we will need it in the next step.7 - Generate some URLs in PythonAs of boto version 2.0 there does not seem to be any support for generating signed CloudFront URLs. Python does not include RSA encryption routines in the standard library so we will have to use an additional library. I've used M2Crypto in this example.For a non-streaming distribution, you must use the full cloudfront URL as the resource, however for streaming we only use the object name of the video file. See the code below for a full example of generating a URL which only lasts for 5 minutes.This code is based loosely on the PHP example code provided by Amazon in the CloudFront documentation.from M2Crypto import EVPimport base64import timedef 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_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, 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_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 encode it (must be URL safe) encoded_policy = aws_url_base64_encode(canned_policy) #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_urldef encode_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 encode themenc_url = encode_query_param(signed_url)print(enc_url)8 - Try out the URLsHopefully you should now have a working URL which looks something like this:video.mp4%3FExpires%3D1309979985%26Signature%3DMUNF7pw1689FhMeSN6JzQmWNVxcaIE9mk1x~KOudJky7anTuX0oAgL~1GW-ON6Zh5NFLBoocX3fUhmC9FusAHtJUzWyJVZLzYT9iLyoyfWMsm2ylCDBqpy5IynFbi8CUajd~CjYdxZBWpxTsPO3yIFNJI~R2AFpWx8qp3fs38Yw_%26Key-Pair-Id%3DAPKAIAZRKVIO4BQPut this into your js and you should have something which looks like this (from the PHP example in Amazon's CloudFront documentation):var so_canned = new SWFObject('http://location.domname.com/~jvngkhow/player.swf','mpl','640','360','9'); so_canned.addParam('allowfullscreen','true'); so_canned.addParam('allowscriptaccess','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');SummaryAs you can see, not very easy! boto v2 will help a lot setting up the distribution. I will find out if it's possible to get some URL generation code in there as well to improve this great library! 这篇关于使用 Python 开始使用安全的 AWS CloudFront 流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 10-24 15:28