如何解决浏览器跨域CORS问题
简介
浏览器同源策略(same-origion policy)
同源策略是Netscape公司在1995年引入浏览器的一个著名安全策略,它是浏览器最核心也最基本的安全功能,可以概括为本域脚本只能读写本域内的资源,而无法访问其它域的资源,以防止信息泄露。例如A, B两个网站属于两个不同的域(比如www.a.com和www.b.com),A网站中的JavaScript脚本就只能访问A网站的资源,而不能访问B网站的资源,因为同源策略的限制使得跨域访问是会被浏览器拒绝的。
所谓同源是指域名相同、协议相同且端口相同。举例如下:
- 不同域名:
http://www.baidu.com
与http://www.baidu.cn
为不同源; - 不同协议:
http://www.baidu.com
与https://www.baidu.com
为不同源; - 不同端口:
http://www.baidu.com:80
与http://www.baidu.com:81
为不同源; - 其它:
http://www.baidu.com/a
与http://www.baidu.com/b
为同源,因为域名协议和端口都相同。
什么是跨域资源共享(CORS)
在实际应用中会经常遇到跨域访问的情况,例如,用户的网站A(www.a.com)后端使用了BOS存储,用户想在该网站的Web应用程序中引用存储在BOS上的资源,但该页面只能请求本域资源,向BOS发送的请求会被浏览器限制,无法直接访问带来不便。为了解决这类跨域访问问题,HTML5提供了一套标准跨域解决方案即CORS。
跨域资源共享(Cross-Origin Resource Sharing,简称 CORS),它是由浏览器共同遵循的一套控制策略,通过HTTP的Header来进行交互。浏览器在识别到发起的请求是跨域请求的时候,会将Origin的Header加入HTTP请求发送给服务器,比如上面那个例子,Origin的Header就是www.a.com。服务器端接收到这个请求之后,会根据一定的规则判断是否允许该来源域的请求。如果允许的话,服务器在返回的响应中会附带上Access-Control-Allow-Origin这个Header,内容为www.a.com来表示允许该次跨域访问。如果服务器允许所有的跨域请求的话,将Access-Control-Allow-Origin的Header设置为*即可,浏览器根据是否返回了对应的Header来决定该跨域请求是否成功,如果没有附加对应的Header,浏览器将会拦截该请求。
CORS需要浏览器和服务器同时支持,整个CORS通信过程,都是浏览器自动完成,不需要用户参与。浏览器一旦发现AJAX请求跨域,就会自动添加一些附加的头信息,有时还会多出一次附加的请求。只要服务器实现了CORS接口,就可以跨域通信。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求是指请求方式是HEAD/GET/POST三种方式中的某一种,而且http头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
不符合以上条件的则是非简单请求。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP OPTIONS查询请求,称为"预检"请求, 请求头信息里包括Origin、Access-Control-Request-Method和Access-Control-Request-Headers三个特殊字段,服务器收到"预检"请求以后,检查了三个字段以后,确认允许跨源请求,就可以做出回应。服务器的回应,都会有一个Access-Control-Allow-Origin头信息字段。
BOS配置CORS
BOS为开发者提供了两种方式来配置bucket资源的跨域访问权限,一种是直接在BOS控制台对bucket进行CORS规则设置;另一种是调用CORS相关的API接口来控制bucket资源的访问权限。
注意:
- BOS中CORS配置是在Bucket级别的;
- CORS请求是否通过和BOS的身份验证是完全独立的,因为CORS规则仅仅是用来决定是否附加CORS相关的Header的一个规则,是否拦截该请求完全由浏览器决定。
-
控制台设置方法:
- 点击“基础配置”页签,选择“跨域访问CORS设置”并点击“修改配置”。
- 点击“确定”,保存规则
-
API控制方法:
- PutBucketCors接口: 用来在指定的Bucket上设定一个跨域资源共享(CORS)的规则,如果原规则存在则覆盖原规则。
- GetBucketCors接口: 用于获取指定的Bucket当前的CORS规则。
- DeleteBucketCors接口: 用于关闭指定Bucket的CORS功能并清空所有规则。
- OPTIONS Object接口: 浏览器在发送跨域请求之前会发送一个preflight请求(OPTIONS)并带上特定的来源域,HTTP方法和Header信息等给BOS以决定是否发送真正的请求,此接口即响应这种请求。
实战案例
以下我们会详细展示简单请求和非简单请求的跨域访问BOS资源时的情况。其中简单请求以GET请求为例, 非简单请求以POST请求为例。
准备条件
- 登录BOS控制台新建一个Bucket,读写权限设置为“公共读写”,然后上传一个Object。在本例中,我们新建一个名为bos-demo的Bucket,然后上传一个bos.txt文件,文件内容为“This is a bos demo for CORS test !”。点击“复制链接”,可以看到bos.txt这个object的访问地址: http://bos-demo.bj.bcebos.com/bos.txt。
-
关闭浏览器cache功能,防止因为浏览器缓存了服务器上次返回的header内容导致和CORS的要求不匹配,影响请求结果,这里我们以chrome浏览器为例, 打开“开发者工具”,勾选“Disable cache”。
跨域请求实例
-
首先用curl访问bos.txt文件,可以发现该object已经可以正常访问。
curl http://bos-demo.bj.bcebos.com/bos.txt
This is a bos demo for CORS test ! - 接着我们尝试使用Fetch API来访问这个object,可以将以下代码复制到本地保存成html文件然后使用浏览器打开,代码中提供了发送GET和POST两种请求的函数。
<!DOCTYPE html>
<html>
<body>
<p align="center" style="font-size: 30px;">
<button onclick="sendGetCorsRequest()">Send Get Request</button>
<button onclick="sendPostCorsRequest()">Send Post Request</button>
</p>
<script type="text/javascript">
var url = "http://bos-demo.bj.bcebos.com/bos.txt";
function sendGetCorsRequest() {
fetch(url).then(function(res) {
if (res.ok) {
alert("The response is ok, get request success!");
} else {
alert("The response wasn't ok, got status ", res.status);
}
}, function(e) {
alert("Get request failed!", e);
});
}
function sendPostCorsRequest() {
fetch(url, { method: "POST" }).then(function(res) {
if (res.ok) {
alert("The response is ok, post request success!");
} else {
alert("The response wasn't ok, got status ", res.status);
}
}, function(e) {
alert("Post request failed!", e);
});
}
</script>
</body>
</html>
打开html文件之后(以chrome为例), 点击“Send Get Request”或者“Send Post Request”按钮,会发现两个请求都发送失败。
通过看chrome Console中报错提示我们看到,是因为没有找到Access-Control-Allow-Origin这个头。显然,这就是因为服务器没有配置CORS。
再进入Header界面,可见浏览器发送了带Origin的Request,因此是一个跨域请求,在Chrome上因为是打开的本地文件所以Origin是null。
配置Bucket CORS规则
CORS设置是由一条一条规则组成的,真正匹配的时候会从第一条开始逐条匹配,以最早匹配上的规则为准。现在添加第一条规则,使用最宽松的配置:
上图的配置代表着所有的Origin都允许访问,所有的请求类型都允许访问,所有的Header都允许,最大的缓存时间为10s,具体参数含义可以参考BOS产品配置文档。配置完成之后重新测试,结果如下:
Get请求结果:
Post请求结果:
可以发现,当我们配置了CORS规则之后,POST和GET请求都可以成功发送了。 除了最宽松的配置之外,还可以配置更精细的控制机制来实现针对性的控制。比如对某个bucket只允许get请求,不允许其它请求,或者只允许某个域名访问该bucket等,这些都可以在控制台进行配置。对于大部分场景来说,用户最好根据自己的使用场景来使用最小的配置以保证安全性。
注意事项
CORS配置项有以下注意事项:
- Origins:配置的时候要带上完整的域信息,不要遗漏协议名如http,如果端口号不是默认的还要带上端口号。如果不确定的话,可以打开浏览器的调试功能查看Origin头。这一项支持使用通配符*,但是只支持一个,可以根据实际需要灵活配置。
- Methods:按照需求开通对应的方法即可。
- Headers:允许通过的Header列表,此建议没有特殊需求的情况下设置为*,大小写不敏感。
- ExposeHeader:暴露给浏览器的Header列表,不允许使用通配符。具体的配置需要根据应用的需求来选择,只暴露需要使用的Header,如ETag等。如果不需要暴露这些信息,这里可以不填。如果有特殊需求可以单独指定,大小写不敏感。