跨域资源共享 --CORS(预检请求和凭据请求)

428 阅读4分钟

本文已参与「[掘力星计划],赢取创作大礼包,挑战创作激励金。

小知识,大挑战!本文正在参与“[程序员必备小知识]创作活动。

前言

跨资源共享(CORS,Cross-Origin Resource Sharing)其实就是浏览器和服务器实现跨源通信。默认情况下,XHR只能访问和发起请求的页面在同一个域的资源。这个安全限制可以防止某些恶意行为,但是浏览器也需要支持合法跨域访问的能力。CORS背后的基本思路就是使用自定义的HTTP头部允许浏览器和服务器互相了解,以确实请求是否成功。

现代浏览器支持CORS

对于简单的请求,比如GET或POST请求,没有自定义头部,而且请求体是text/plain类型,这样的请求在发送时会有一个额外的头部叫Origin。Origin头部包含发送请求的页面的源(协议、域名和端口),以便服务器确定是否为其提供响应。下面是origin头部的一个示例: Origin: http://www.nczonline.net 如果服务器决定响应请求,那么点该发送 Access-Control-Allow-Origin头部,包含相同的源;或者如果资源是公开的、那么就包含"*"。比如: Access-Control-Allow-Oriain: httn://www.nczonline.net 如果没有这个头部、或者右但源不匹配,则表明不会响应浏览器请求。否则,服务器就会处理这个请求。注意,无论请求还是响应都不会包含cookie信息。

现代浏览器通过XMLHttpReauest对象原生支持 CORS。在尝试访问不同源的资源时,这个行为会被自动触发。要向不同域的源发送请求.可以使用标准XHR对象并给open()方法传入一个绝对URL,比如:

let xhr = new XMLHttpRequest();
xhr.open("get","http://www.somepersons.com/page/",true);
xhr.onreadystatechange = function(){
if(xhr.readyState==4){
  if(xhr.status>=200 && xhr.status<300||xhr.status==304){
  console.log(xhr.responseText);
  }else{
  console.log('Response was wrong:' + xhr.status);
  }
  }
  };
  xhr.send(null);
  

跨域XHR对象允许访问status和statusText属性,也允许同步请求。出于安全考虑,跨域X对象也施加了一些额外限制。

  1. 不能使用setRequestHeader()设置自定义头部。
  2. 不能发送和接收cookie。
  3. getA1lResponseHeaders()方法始终返回空字符串。

因为无论同域还是跨域请求都使用同一个接口,所以最好在访问本地资源时使用相对URL,在远程资源时使用绝对 URL。这样可以更明确地区分使用场景,同时避免出现访问本地资源时出现头部或cookie信息访问受限的问题。

预检请求

CORS通过一种叫预检请求(preflighted request)的服务器验证机制,允许使用自定义头部、除GET和POST之外的方法,以及不同请求体内容类型。在要发送涉及上述某种高级选项的请求时,会先向服务器发送一个“预检”请求。这个请求使用OPTIONS方法发送并包含以下头部。

  • Origin:与简单请求相同。
  • Access-Control-Request-Method:请求希望使用的方法。
  • Access-Control-Request-Headers:(可选)要使用的逗号分隔的自定义头部列表。 下面是一个假设的POST 请求,包含自定义的NCz头部:
  • Origin: www.nczonline.net
  • Access-Control-Request-Method: POST
  • Access-Control-Request-Headers:NCZ

在这个请求发送后,服务器可以确定是否允许这种类型的请求。服务器会通过在响应中发送如下头部与浏览器沟通这些信息。

  • DAccess-Control-Al1ow-Oriain:与简单请求相同。
  • Access-Control-Allow-Met hods:允许的方法(逗号分隔的列表)。
  • Access-Control-allow-Headers:服务器允许的头部(逗号分隔的列表)。
  • Access-Control-Max-Age:缓存预检请求的秒数。 例如:
  • Access-Control-Allow-origin:www.nczonline.net
  • Access-Control-Al lOw-Methods: POST, GET
  • Access-Control-Al1ow-Headers: NCZ
  • Access-Control-Max-Age: 1728000 预检请求返回后,结果会按响应指定的时间缓存一段时间。换句话说,只有第一次发送这种类型的请求时才会多发送一次额外的HTTP 请求。

凭据请求

默认情况下,跨源请求不提供凭据( cookie , HTTP认证和客户端 SSL 证书)。可以通过将withCredentials属性设置为true来表明请求会发送凭据。如果服务器允许带凭据的请求,那么可以在响应中包含如下HTTP头部: Access-Control-Allow-Credentials: true

如果发送了凭据请求而服务器返回的响应中没有这个头部,则浏览器不会把响应交给JavaScript (responseText是空字符串,status是0,onerror()被调用)。注意,服务器也可以在预检请求在 响应中发送这个HTTP头部,以表明这个源允许发送凭据请求。

下一章我们讲替代性跨域技术。