背景

当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,会发起一个跨域 HTTP 请求。

比如,站点 http://domain-a.com 的某 HTML 页面通过 <img> 的 src 请求 http://domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。

出于安全考虑,浏览器会限制从脚本内发起的跨域HTTP请求。例如,XMLHttpRequest 和 Fetch 遵循同源策略。

跨域并非不一定是浏览器限制了发起跨站请求,而也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。最好的例子是 CSRF 跨站攻击原理,请求是发送到了后端服务器无论是否跨域!

注意:有些浏览器不允许从 HTTPS 的域跨域访问 HTTP,比如  Chrome 和 Firefox,这些浏览器在请求还未发出的时候就会拦截请求,这是一个特例。、

跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest 或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。

下列场景允许使用跨域 HTTP 请求:

  1. 由 XMLHttpRequest 或 Fetch 发起的跨域 HTTP 请求。
  2. Web 字体 (CSS 中通过 @font-face 使用跨域字体资源), 因此,网站就可以发布 TrueType 字体资源,并只允许已授权网站进行跨站调用。
  3. WebGL 贴图
  4. 使用 drawImage 将 Images/video 画面绘制到 canvas
  5. 样式表(使用 CSSOM)
  6. Scripts (未处理的异常)

利用fetch跨域请求时候,除了get请求。其他post,put,delete都会发起一个预检option请求,当服务器允许跨域时,才真正发起真正的请求,在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)

如果设置withCredentials,Access-Control-Allow-Origin: * 就不能用,必须设置指定的origin

预检请求

预检请求要求必须首先使用 OPTIONS   方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求"的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。

当请求满足下述任一条件时,即应首先发送预检请求:

使用了下面任一 HTTP 方法:

设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

 Content-Type 的值不属于下列之一:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

跨站请求的过程如下图,先发送一个option请求服务器确认后发送具体的请求

http响应头

服务器可以通过设置响应头来实现跨域访问,设置如下

app.all('*', function(req, res, next) {  
    res.header("Access-Control-Allow-Origin", "*");  
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");  
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");  
    next();  
});