CORS 是一个 W3C 标准,全称是“跨域资源共享”(Cross-origin resource sharing)。
默认浏览器为了安全,遵循“同源策略”,不允许 Ajax 跨域访问资源,而为了允许这种操作,服务器端和客户端都要遵循一些约定。

服务器端需设置以下响应头:

  1. Access-Control-Allow-Origin: | * // 授权的访问源
  2. Access-Control-Max-Age: // 预检授权的有效期,单位:秒
  3. Access-Control-Allow-Credentials: true | false // 是否允许携带 Cookie
  4. Access-Control-Allow-Methods: [, ]* // 允许的请求动词
  5. Access-Control-Allow-Headers: [, ]* // 额外允许携带的请求头
  6. Access-Control-Expose-Headers: [, ]* // 额外允许访问的响应头

我们看到,Access-Control-Allow-Credentials 响应头会使浏览器允许在 Ajax 访问时携带 Cookie,但我们仍然需要对 XMLHttpRequest 设置其 withCredentials 参数,才能实现携带 Cookie 的目标。
示例代码如下:
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
注意,为了安全,标准里不允许 Access-Control-Allow-Origin: *,必须指定明确的、与请求网页一致的域名。同时,Cookie 依然遵循“同源策略”,只有用目标服务器域名设置的 Cookie 才会上传,而且使用 document.cookie 也无法读取目标服务器域名下的 Cookie。
下面简单普及一下 CORS 的有关知识。
浏览器对待 CORS 有两种规则,一种只有一次请求,一种要多发送一次“预检查”请求。
简单 CORS
同时满足以下条件:
动词限制,只允许是:

  1. HEAD
  2. GET
  3. POST

请求头限制,只允许出现:

  1. Accept
  2. Accept-Language
  3. Content-Language
  4. Last-Event-ID

Content-Type 且只允许是:

  1. application/x-www-form-urlencoded
  2. multipart/form-data
  3. text/plain

简单请求浏览器会直接发送该请求,并附加 Origin 头,比如:

  • Origin: localhost:8080

服务器会返回:

  • Access-Control-Allow-Origin: http://localhost:8080
  • Access-Control-Max-Age: 600
  • Access-Control-Allow-Credentials: true
  • Access-Control-Expose-Headers: X-Custom-Header

浏览器在收到服务器返回时,先检查是否允许访问,不允许则直接报错(可用 XMLHttpRequest.onerror 捕获)。
带预检查的 CORS
不满足简单 CORS 要求的请求,在正式请求前,浏览器会发送一次 OPTIONS 动词的请求。
例如欲请求 PUT /xxx,想额外发送 X-Custom-Header 头,则会先发送:

  1. OPTIONS /xxx HTTP/1.1
  2. Origin: http://api.bob.com
  3. Access-Control-Request-Method: PUT
  4. Access-Control-Request-Headers: X-Custom-Header
  5. Host: localhost:8080

服务器返回:

  1. HTTP/1.1 200 OK
  2. Access-Control-Allow-Credentials: true
  3. Access-Control-Allow-Headers: X-Custom-Header
  4. Access-Control-Allow-Methods: PUT
  5. Access-Control-Allow-Origin: http://localhost:8080
  6. Access-Control-Max-Age:600 Allow: GET, HEAD, POST, PUT, DELETE,
    TRACE, OPTIONS, PATCH
  7. Vary: Origin

浏览器检查完,一切顺利,才发送真正的请求。

标签: none

添加新评论