浏览器为什么要阻止跨域请求?如何解决跨域?每次跨域请求都需要到达服务端吗?

为什么存在跨域?

为了安全采用浏览器同源策略的限制, 防止csrf攻击。无法共享不同域名的资源,同时也限制了DOM的读取

  1. 协议
  2. 域名
  3. 端口

同源策略又分为以下两种

  1. DOM同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。
    2. XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。(J基本的。发起的每一次HTTP请求都会带上请求地址对应的cookie.)

解决方案:

服务器代理, 如nginx

跨域资源共享 CORS

CORS, Cross-origin resource sharing

  1. 简单请求。HEAD、GET、POST请求方法且请求头相对简单。

客户端

  1. var xhr = new XMLHttpRequest();
  2. xhr.withCredentials = true;

服务端

  1. Access-Control-Allow-Origin: http://api.bob.com 必填字段
  2. Access-Control-Allow-Credentials: true
  3. Access-Control-Expose-Headers: FooBar
  1. 复杂请求。PUT、DELETE请求方法, 或者Content-Type: application/json。

不同在于, 通信前, 先进行预请求。
客户端

  1. var xhr = new XMLHttpRequest();
  2. xhr.open('PUT', 'http://api.alice.com/cors', true);
  3. xhr.setRequestHeader('X-Custom-Header', 'value'); // 添加自定义头信息

请求头部

  1. Access-Control-Request-Method: PUT
  2. Access-Control-Request-Headers: X-Custom-Header

服务端

  1. Access-Control-Allow-Methods: GET, POST, PUT 必填字段
  2. Access-Control-Allow-Headers: X-Custom-Header 必填字段
  3. Access-Control-Allow-Credentials: true
  4. Access-Control-Max-Age: 1728000

JSONP

基本原理就是通过动态创建script标签,然后利用src属性进行跨域。
限制: 仅支持get请求

  1. var script = document.createElement('script');
  2. script.src = 'demo.js?callback=fun';
  3. document.getElementsByTagName('body')[0].appendChild(script);

document.domain跨子域

适用于:主域名相同,子域名不同。

location.hash

跨域修改父框架src的hash值
限制: 传递数据字节有限

  1. window.name+iframe
    window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。

    window.postMessage

  2. h5特性window.postMessage()页面之间的通信

  3. h5特性WebSocket
  4. websocket是一种不同于http的新的协议,它实现了浏览器与服务器全双工通信,同时允许跨域通讯

参考:
阮一峰:CORS详解
https://zhuanlan.zhihu.com/p/28562290