使用背景
当您使用 CDN 回源到 OSS 遇到跨域问题时,可以尝试先用脚本AnalysisCors进行初步排查和定位。使用之前请安装 pip install oss2 模块
使用方法
[root@izwx4z api]# python AnalysisCore.py -h
Usage: AnalysisCore.py [options] 都是必选项
Options:
-h, --help show this help message and exit
-i AK <Accesskey> 阿里云的云账号或者具备操作 OSS 权限的 RAM 子账号,Accesskey
-k SK <AccessKeySecrety> 阿里云的云账号或者具备操作 OSS 权限的 RAM 子账号,Accesskey Secrety
-e ED dest oss <endpoint> OSS endpoint,可以从控制台上获取
-b BK dest oss <bucket> OSS bucket
-s CDN cors allow <domain> 跨域访问 OSS 的 CDN 域名,必须携带协议头 HTTPS/HTTP
-m METH cors http method <GET,POST,PUT,HEADER> cors http method <GET,POST,PUT,HEADER> 指定跨域访问的 HTTP 请求方式,多个请求方法请用 "," 隔开
-t TYPES cors file type multiparts operate or single operate <multi,single> 您可以通过单一文件(single)或者大文件分片(multi)的方式将文件上传、下载到 OSS
CDN 回源到 OSS 遇到跨域问题,通常有以下四种情况:
-
第一种:
python cors.py -i LTA43W -k VTqIcMlVNc -e oss-cn-beijing.aliyuncs.com -b yourBucket -s http://www.zhangyb.com -t multi -m POST
OSS Cors 设置 Access-Control-Allow-Origin Config 没有携带跨域头。
这表明在OSS中执行了分片上传、下载操作,但是没有配置暴露 Headers,<Etag|etag|*>
CDN跨域访问OSS失败的原因以及处理方法
-
第二种:
python cors.py -i LTA43W -k VTqIcMlVNc -e oss-cn-beijing.aliyuncs.com -b yourBucket -s http://www.zhangyb.com -t multi -m POST,DELETE,HEADE
CDN 回源到 OSS 的跨域请求方式没有在 OSS Cors Access-Control-Allow-Methods 中进行配置。
CDN跨域访问OSS失败的原因以及处理方法
-
第三种:
python cors.py -i LTA43W -k VTqIcMlVNc -e oss-cn-beijing.aliyuncs.com -b yourBucket -s http://www.zhangyb.com -t multi -m POST,DELETE,HEADE
上传、下载大文件操作时,如果是分片 (multipart) 请求,需要暴露 ETag 头。
CDN跨域访问OSS失败的原因以及处理方法
-
第四种:
python cors.py -i LTA43W -k VTqIcMlVNc -e oss-cn-beijing.aliyuncs.com -b yourBucket -s http://www.zhangyb.com,http://www.taobao.com -t multi -m POST,DELETE,HEADE
跨域的域名没有在 OSS Cors Access-Control-Allow-Origin 进行配置。
源码
#-*- coding:utf8 -*-
#!/usr/bin/env python
#Author: hanli
#Update: 2018-09-22
from optparse import OptionParser
import collections
import oss2
import sys
import re
class MainFunction():
def __init__(self,options):
self.args = collections.OrderedDict()
self.args["ak"] = options.ak
self.args["sk"] = options.sk
self.args["ed"] = options.ed
self.args["bk"] = options.bk
self.args["cdn"] = options.cdn
self.args["types"] = options.types
self.args["meth"] = options.meth
self.left = '/033[1;31;40m'
self.right = '/033[0m'
# check input parse
def CheckParse(self):
try:
for keys in self.args:
if self.args[keys] == None:
self.ConsoleLog(0)
return False
if not (self.args['ed'].startswith("http://") or self.args['ed'].startswith("https://")):
self.args['ed'] = "http://"+self.args['ed']
if not (self.args['cdn'].startswith("http://") or self.args['cdn'].startswith("https://")):
self.ConsoleLog(1)
return False
if not re.match("(GET|POST|PUT|HEAD|DELETE|ORIGIN)",self.args['meth']):
self.ConsoleLog(7)
return False
if not re.match("multi|single",self.args['types']):
self.ConsoleLog(2)
return False
except Exception as e:
self.ConsoleLog(e)
return True
# valid cors config
def ValidConf(self,origins,methods,headers,expose,flag=True):
try:
if not (re.match(self.args['cdn'],origins) or origins=="*"):
flag=False
self.ConsoleLog(3)
for meth in self.args['meth'].split(","):
if not (meth in methods.split(",") or methods=="*"):
flag=False
self.ConsoleLog(4,meth)
if self.args['types'] == "multi":
if not (re.match("(etag|Etag| )",expose)):
flag=False
self.ConsoleLog(6)
if headers == None:
flag=False
self.ConsoleLog(5)
except Exception as e:
self.ConsoleLog(e)
return flag
# get oss cors config
def GetCors(self):
try:
auth = oss2.Auth(self.args['ak'], self.args['sk'])
bucket = oss2.Bucket(auth, self.args['ed'], self.args['bk'])
cors = bucket.get_bucket_cors()
except oss2.exceptions.ServerError as e:
self.ConsoleLog(e)
except oss2.exceptions.NoSuchCors as e:
self.ConsoleLog(e)
except oss2.exceptions.NoSuchBucket as e:
self.ConsoleLog(e)
except oss2.exceptions.AccessDenied as e:
self.ConsoleLog(e)
else:
for rule in cors.rules:
if (self.ValidConf(",".join(rule.allowed_origins),",".join(rule.allowed_methods),
",".join(rule.allowed_headers),",".join(rule.expose_headers))):
self.ConsoleLog(8)
# output error log
def ConsoleLog(self,level,meth=None):
if level == 0:
print('{0}[ERROR]{1}All parameters cannot be empty'.format(self.left,self.right))
if level == 1:
print('{0}[ERROR]{1}<-s> CDN domain must be http|https start'.format(self.left,self.right))
if level == 2:
print('{0}[ERROR]{1}<-t> Must be multi|single'.format(self.left,self.right))
if level == 3:
print('{0}[CheckResult]{1}OSS Access-Control-Allow-Origin Config Error {2}, You can fill in * or http://domain'.format(self.left,self.right,self.args['cdn']))
if level == 4:
print('{0}[CheckResult]{1}OSS Access-Control-Allow-Methods Config Error {2}, You can fill in * or methods'.format(self.left,self.right,meth))
if level == 5:
print('{0}[CheckResult]{1}Access-Control-Allow-Headers OSS Config Error, You can fill in * or headers'.format(self.left,self.right))
if level == 6:
print('{0}[CheckResult]{1}Multipart operate must set expose <etag|Etag|*>'.format(self.left,self.right))
if level == 7:
print('{0}[ERROR]{1}<-m> Must be <GET|POST|PUT|HEAD|DELETE|ORIGIN> ,multiple method please "," split'.format(self.left,self.right))
if level == 8:
sys.exit("[CheckResult]OSS CORS Config Alright OK")
if not isinstance(level,int):
print(level)
# expr1 = lambda x:x if x.startswith("http://") or x.startswith("https://") else "https://"+x
# expr2 = lambda y:y if y.startswith("http://") or y.startswith("https://") else "https://"+y
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-i",dest="ak",help="<Accesskey>")
parser.add_option("-k",dest="sk",help="<AccessKeySecrety>")
parser.add_option("-e",dest="ed",help="dest oss <endpoint>")
parser.add_option("-b",dest="bk",help="dest oss <bucket>")
parser.add_option("-s",dest="cdn",help="cors allow <domain>")
parser.add_option("-m",dest="meth",help="cors http method <GET,POST,PUT,HEADER>")
parser.add_option("-t",dest="types",help="cors file type multiparts operate or single operate<multi,single>")
(options, args) = parser.parse_args()
handler = MainFunction(options)
if handler.CheckParse():
handler.GetCors()