如何解决浏览器跨域CORS问题
概述
浏览器同源策略(same-origin 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 头信息不超出以下几种字段:
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-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 规则的 Bucket。
- 已准备可通过公开 URL 读取的 Object,用于验证浏览器跨域访问效果。原文参考 Object 访问地址为 http://bos-demo.bj.bcebos.com/bos.txt。,实际操作时请替换为自己的 Object 地址。
- 如需复现实战案例,可上传一个文本 Object,文件内容可设置为
This is a bos demo for CORS test !。 - 使用 Chrome 等浏览器进行验证时,建议打开开发者工具并启用 Disable cache,避免浏览器缓存服务器上次返回的 Header 内容,影响请求结果。## 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访问已准备好的 Object 文件,确认该 Object 可以正常访问。
1curl http://<bucket-name>.<region>.bcebos.com/<object-key>
响应示例:
1This is a bos demo for CORS test !
- 接着使用 Fetch API 访问该 Object。将以下代码复制到本地并保存成 HTML 文件,然后使用浏览器打开。代码中提供了发送
GET和POST两种请求的函数,请将<object-url>替换为实际 Object 访问地址。
1<!DOCTYPE html>
2<html>
3<body>
4 <p align="center" style="font-size: 30px;">
5 <button onclick="sendGetCorsRequest()">Send Get Request</button>
6 <button onclick="sendPostCorsRequest()">Send Post Request</button>
7 </p>
8
9 <script type="text/javascript">
10 var url = "<object-url>";
11
12 function sendGetCorsRequest() {
13 fetch(url).then(function(res) {
14 if (res.ok) {
15 alert("The response is ok, get request success!");
16 } else {
17 alert("The response wasn't ok, got status " + res.status);
18 }
19 }, function(e) {
20 alert("Get request failed! " + e);
21 });
22 }
23
24 function sendPostCorsRequest() {
25 fetch(url, { method: "POST" }).then(function(res) {
26 if (res.ok) {
27 alert("The response is ok, post request success!");
28 } else {
29 alert("The response wasn't ok, got status " + res.status);
30 }
31 }, function(e) {
32 alert("Post request failed! " + e);
33 });
34 }
35 </script>
36</body>
37</html>
- 打开 HTML 文件后,页面显示 【Send Get Request】 和 【Send Post Request】 两个按钮。

- 点击 【Send Get Request】。如果 Bucket 未配置 CORS 规则,浏览器会拦截该跨域请求,并提示缺少
Access-Control-Allow-Origin响应头。

- 点击 【Send Post Request】。如果 Bucket 未配置 CORS 规则,浏览器同样会拦截该跨域请求。使用本地 HTML 文件打开页面时,浏览器请求来源通常显示为
origin 'null',这是本地文件跨域请求的正常表现。


该现象说明:Object 可以被直接访问,但 Bucket 未配置 CORS 时,浏览器通过 Fetch API 发起的跨域请求会失败。
配置Bucket CORS规则
CORS设置是由一条一条规则组成的,真正匹配的时候会从第一条开始逐条匹配,以最早匹配上的规则为准。现在添加第一条规则,使用最宽松的配置:

上图的配置代表着所有的Origin都允许访问,所有的请求类型都允许访问,所有的Header都允许,最大的缓存时间为10s,具体参数含义可以参考BOS产品配置文档。配置完成之后重新测试,结果如下:
Get请求结果:


Post请求结果:


可以发现,当我们配置了CORS规则之后,POST和GET请求都可以成功发送了。 除了最宽松的配置之外,还可以配置更精细的控制机制来实现针对性的控制。比如对某个bucket只允许get请求,不允许其它请求,或者只允许某个域名访问该bucket等,这些都可以在控制台进行配置。对于大部分场景来说,用户最好根据自己的使用场景来使用最小的配置以保证安全性。
注意事项
CORS 配置项有以下注意事项:
- Origins:配置时要带上完整的域信息,不要遗漏协议名,如
http;如果端口号不是默认端口,还要带上端口号。如果不确定,可以打开浏览器调试功能查看Origin头。这一项支持使用通配符*,但是只支持一个,可以根据实际需要灵活配置。 - Methods:按照需求开通对应的方法即可。
- Headers:允许通过的 Header 列表。没有特殊需求时,建议设置为
*,大小写不敏感。 - ExposeHeaders:暴露给浏览器的 Header 列表,不允许使用通配符。具体配置需要根据应用需求来选择,只暴露需要使用的 Header,如
ETag等。如果不需要暴露这些信息,可以不填;如果有特殊需求,可以单独指定,大小写不敏感。
评价此篇文章
