跨域的解决方法之一:CORS跨域资源共享

781 阅读2分钟

简介

CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。 因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

请求

1. 简单请求

image.png

  • 浏览器在头信息里自动添加origin
  • 如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
    • Access-Control-Allow-Origin: http://api.bob.com
      • (可以是* ,允许所有跨域)
    • Access-Control-Allow-Credentials: true
      • (是否可以发送cookie,不要就删除这个字段,默认cookie不包含在CORS请求中)
    • Access-Control-Expose-Headers: FooBar
      • (getResponseHeader 默认获取的6个字段不包含自定义头的,通过getResponseHeader('FooBar')获取)
    • Content-Type: text/html; charset=utf-8
  • withCredentials 属性
    • 服务端要同意: Access-Control-Allow-Credentials: true
    • 开发者要在AJAX请求中打开这个属性:xhr.withCredentials = true;

注意!!!: 如果要发送Cookie,Access-Control-Allow-Origin就不能设为*,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。

2. 非简单请求:多一次HTTP查询请求

要求:

  • PUT
  • DELETE
  • content-type: application/json

会在通信前问下服务器,所在网页是否在服务器的许可名单内,通过了才能才出正式的XMLHttpRequest请求。

注意!!:如果服务器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段,浏览器会认定服务器不同意预检,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200。

预检请求的HTTP头信息

关键:

  • Origin: http://api.bob.com (发送的源)
  • Access-Control-Request-Method: PUT (请求方法)
  • Access-Control-Request-Headers: X-Custom-Header (自定义头信息)
    OPTIONS /cors HTTP/1.1
    Origin: http://api.bob.com
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: X-Custom-Header
    Host: api.alice.com
    Accept-Language: en-US
    Connection: keep-alive
    User-Agent: Mozilla/5.0...

预检请求的回应

关键:

  • Access-Control-Allow-Origin: * (允许所有跨域)
    HTTP/1.1 200 OK
    Date: Mon, 01 Dec 2008 01:15:39 GMT
    Server: Apache/2.0.61 (Unix)
    Access-Control-Allow-Origin: http://api.bob.com
    Access-Control-Allow-Methods: GET, POST, PUT(表明服务器支持的所有跨域请求的方法)
    Access-Control-Allow-Headers: X-Custom-Header
    Content-Type: text/html; charset=utf-8
    Content-Encoding: gzip
    Content-Length: 0
    Keep-Alive: timeout=2, max=100
    Connection: Keep-Alive
    Content-Type: text/plain
    Access-Control-Max-Age: 1728000 
    //(用来指定本次预检请求的有效期,在此期间,不用发出另一条预检请求。)