[关闭]
@wuxuejun 2016-02-18T09:06:14.000000Z 字数 4722 阅读 1826

跨域资源共享(CORS)使用指南 V1.0


1、业务背景

跨域问题一直是开发工程师,特别是前端工程师关心的问题。主要的问题是安全限制(也即同源策略,即 JavaScript 或 Cookie 只能访问同域下的内容)。跨域访问,简单的说就是 A 网站的 JavaScript 代码试图访问 B 网站,包括提交内容和获取内容。由于安全原因,跨域访问默认是被各浏览器所默认禁止的。在广域网环境中,由于浏览器的安全限制,网络连接的跨域访问是不被允许的,XmlHttpRequest 也不例外,但有时候跨域访问资源是必需的。

关于跨域问题,目前的解决方案有:

  • 方式一:可以通过 JSONP、Flash 或者服务器中转的方式来实现
  • 方式二:通过 CORS 来实现

CORS 与 JSONP 相比,无疑更为先进、方便和可靠。具体表现为:

  • JSONP 只能实现 GET 请求,而 CORS 支持所有类型的HTTP请求。
  • 使用 CORS,开发者可以使用普通的 XMLHttpRequest 发起请求和获得数据,比起 JSONP 有更好的错误处理。
  • JSONP 主要被老的浏览器支持,它们往往不支持 CORS,而绝大多数现代浏览器都已经支持了 CORS(这部分会在后文浏览器支持部分介绍)

本文只介绍 CORS 解决方案。

2、什么是 CORS

跨域资源共享 ( Cross-Origin Resource Sharing)是一种网络浏览器的技术规范,他为 Web 服务器定义了一种方式,允许网页从不同的域访问其资源。隶属于 W3C 的 Web 应用工作组。是一种让 Web 应用服务器能够支持跨站访问控制的机制,从而使得安全的进行跨域数据传输变得可能。跨源资源共享标准通过新增一系列 HTTP 头,让服务器能声明那些来源可以通过浏览器访问该服务器上的资源。另外,对那些会对服务器数据造成破坏性影响的 HTTP 请求方法(特别是 GET 以外的 HTTP 方法,或者搭配某些MIME类型的POST请求),标准强烈要求浏览器必须先以 OPTIONS 请求方式发送一个预请求(preflight request),从而获知服务器端对跨源请求所支持 HTTP 方法。在确认服务器允许该跨源请求的情况下,以实际的 HTTP 请求方法发送那个真正的请求。服务器端也可以通知客户端,是不是需要随同请求一起发送信用信息(包括 Cookies 和 HTTP 认证相关数据)。

3、使用场景

  • 使用 XMLHttpRequest 发起跨站 HTTP 请求。
  • Web 字体 (CSS 中通过 @font-face 使用跨站字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
  • WebGL 贴图 ( WebGL可以为 HTML5 Canvas 提供硬件 3D 加速渲染)。
  • 使用 drawImage API 在 canvas 上画图。

4、应用示例

比如说,假如站点 http://test.abc.com 的网页应用想要访问 http://test.efg.com 的资源。以下的 JavaScript 代码应该会在 test.abc.com 上执行:

  1. var invocation = new XMLHttpRequest();
  2. var url = 'http://test.efg.com/resources/test-data/';
  3. function callOtherDomain() {
  4. if(invocation) {
  5. invocation.open('GET', url, true);
  6. invocation.onreadystatechange = handler;
  7. invocation.send();
  8. }
  9. }

让我们看看,在这个场景中,浏览器会发送什么的请求到服务器,而服务器又会返回什么给浏览器:

Request 头部分:

  1. > GET /resources/test-data/ HTTP/1.1
  2. > Host: test.abc.com
  3. > User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130Minefield/3.1b3pre
  4. > Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  5. > Accept-Language: en-us,en;q=0.5
  6. > Accept-Encoding: gzip,deflate
  7. > Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
  8. > Connection: keep-alive
  9. > Referer: http://test.abc.com/examples/access-control/simpleXSInvocation.html
  10. > Origin: http://test.abc.com

第 1~10 行是 Firefox 3.5 发出的请求头。注意看第 10 行的请求头 Origin,它表明了该请求来自于 http://test.abc.com

Response 头部分:

  1. > HTTP/1.1 200 OK
  2. > Date: Mon, 01 Dec 2008 00:23:53 GMT
  3. > Server: Apache/2.0.61
  4. > Access-Control-Allow-Origin: *
  5. > Keep-Alive: timeout=2, max=100
  6. > Connection: Keep-Alive
  7. > Transfer-Encoding: chunked
  8. > Content-Type: application/xml

第 1~8 行则是 http://test.efg.com 服务器的响应。如第 4 行所示,服务器返回了响应头 Access-Control-Allow-Origin: *,这表明服务器接受来自任何站点的跨站请求。如果服务器端仅允许来自 http://test.abc.com 的跨站请求,它可以返回:
Access-Control-Allow-Origin: http://test.abc.com
现在,除了 http://test.abc.com,其它站点就不能跨站访问 http://test.efg.com 的资源了。
如上,通过使用 Origin 和 Access-Control-Allow-Origin 就可以完成最简单的跨站请求。不过 Access-Control-Allow-Origin 需要为 * 或者包含由 Origin 指明的站点。

5、使用方式

作为一个云服务提供商, UPYUN 为了解决跨域问题,支持 CORS 的配置,服务器端需要返回相应的响应头信息。设置项具体包括:

  • Access-Control-Allow-Origin ( 来源 )
  • Access-Control-Allow-Methods ( Methods )
  • Access-Control-Allow-Headers( Allow Headers )
  • Access-Control-Expose-Headers ( Expose Headers )
  • Access-Control-Max-Age ( 缓存时间 )

相关描述:

  • 来源 :允许跨域的 Origin 列表
  • Method :允许跨域的方法列表,按照需求开通对应的方法即可
  • Allow Header :允许跨域的 Header 列表
  • Expose Header :允许暴露给 JavaScript 代码的 Header 列表
  • 缓存时间 :最大的浏览器缓存时间,单位秒

接下来就说说每个配置项的使用方法:

(1) Access-Control-Allow-Origin

返回的资源需要有一个 Access-Control-Allow-Origin 头信息,语法结构如下:Access-Control-Allow-Origin: | *

origin 参数指定一个允许向该服务器提交请求的 URI 。对于一个不带有 credentials 的请求,可以指定为'',表示允许来自所有域的请求。这种设置相对宽泛,如果对安全性要求比较高的话,不建议这么设置。举个例子,允许来自 http://test.abc.com 的请求,你可以这样指定:Access-Control-Allow-Origin: http://test.abc.com。如果服务器端指定了域名,而不是 '' ,那么响应头的 Vary 值里必须包含 Origin.它告诉客户端: 响应是根据请求头里的 Origin 的值来返回不同的内容的。

(2) Access-Control-Allow-Methods

指明资源可以被请求的方式有哪些(一个或者多个). 这个响应头信息在客户端发出预检请求的时候会被返回。支持的方法有:GET,HEAD,PUT,POST,DELETE,OPTIONS

(3)Access-Control-Allow-Headers

  • Access-Control-Allow-Headers (Allow Headers)

也是在响应预检请求的时候使用.用来指明在实际的请求中,可以使用哪些自定义HTTP请求头.比如
Access-Control-Allow-Headers: X-PINGOTHER
这样在实际的请求里,请求头信息里就可以有这么一条:
X-PINGOTHER: pingpong
可以有多个自定义HTTP请求头,用逗号分隔.
Access-Control-Allow-Headers: [, ]*

(4)Access-Control-Expose-Headers

  • Access-Control-Expose-Headers (Expose Headers)
    (Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

设置浏览器允许访问的服务器的头信息的白名单:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

这样, X-My-Custom-Header 和 X-Another-Custom-Header这两个头信息,都可以被浏览器得到.

(5)Access-Control-Max-Age

  • Access-Control-Max-Age (缓存时间)

这个头告诉我们这次预请求的结果的有效期是多久,如下:
Access-Control-Max-Age: 600

600 这里表示 600 秒,允许这个预请求的参数缓存的秒数,在此期间,不用发出另一条预检请求. Access-Control-Allow-Credentials
告知客户端,当请求的credientials属性是true的时候,响应是否可以被得到.当它作为预请求的响应的一部分时,它用来告知实际的请求是否使用了credentials.注意,简单的GET请求不会预检,所以如果一个请求是为了得到一个带有credentials的资源,而响应里又没有Access-Control-Allow-Credentials头信息,那么说明这个响应被忽略了.
Access-Control-Allow-Credentials: true | false

6、注意事项

  • 最多支持 10 条匹配规则,匹配的时候会从第一条开始逐条匹配,以最早匹配上的规则为准;来源、Allow Header 、Expose Header 最多允许 20 行。
  • 来源及 Method 建议据需要使用最小的配置来保证安全性,多条规则间不要有覆盖和冲突。
  • 针对每个可能的访问来源单独配置规则,尽量不要将多个来源混在一个规则中,多个规则之间最好不要有覆盖冲突,其他的权限只开放需要的权限即可。
  • Allow Header 建议没有特殊需求的情况下设置为 *,大小写不敏感。
  • Expose Header 不允许使用通配符,如果有特殊需求可以单独指定,大小写不敏感。
  • 缓存时间如果没有特殊情况可以酌情设置得大一点,默认为 86400 秒,最大为 604800 秒。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注