本文已参与「[掘力星计划],赢取创作大礼包,挑战创作激励金。
小知识,大挑战!本文正在参与“[程序员必备小知识]创作活动。
前言
跨资源共享(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对象也施加了一些额外限制。
- 不能使用setRequestHeader()设置自定义头部。
- 不能发送和接收cookie。
- 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头部,以表明这个源允许发送凭据请求。
下一章我们讲替代性跨域技术。