Featured image of post 跨域

跨域

浏览器跨域及解决跨域的几种方式整理

跨域

什么是跨域

在解释跨域之前,需先了解同源策略

同源策略

指在 Web浏览器 中允许某个网页 脚本 访问另一个网页数据。前提是两个网页具有相同的 URI、域名/主机、端口号

关于 URI、域名和端口,请看以下示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
               hierarchical part
        ┌───────────────────┴─────────────────────┐
                    authority               path
        ┌───────────────┴───────────────┐┌───┴────┐
  abc://username:[email protected]:123/path/data?key=value&key2=value2#fragid1
  └┬┘   └───────┬───────┘ └────┬────┘ └┬┘           └─────────┬─────────┘ └──┬──┘
scheme  user information     host     port                  query         fragment

  urn:example:mammal:monotreme:echidna
  └┬┘ └──────────────┬───────────────┘
scheme              path

一般判定同源只需要判定三个部分——scheme,host,port

因此通常情况下 URI 指的是——协议(scheme)

关于浏览器请求的协议部分,内容比较多,我将另起一个章节

为什么会有同源策略

由于 Web应用程序 广泛依赖于 HTTP Cookie 来维持会话,出于安全性考虑,必须将不相关的网站严格分离,以防止数据丢失泄露

因此,有了同源策略的概念之后,什么是跨域也呼之欲出了。是的,所有不满足同源策略的访问,都是跨域

如何实现跨域访问

jsonp

  • 原理:同源策略只限制在 JS 脚本,HTML 的标签是不受约束的

  • 应用场景:CDN 在线资源请求

  • 实现方式

    1
    2
    3
    4
    5
    6
    
    <!--- 如果是图片、视频等 ---!>
    <img src="http:exampe.test.com:80/back.png" />
    <video src="http:exampe.test.com:80/demo.mp4" />
    
    <!--- 如果是JSON、JS等 ---!>
    <script type="text/java"  src="http:exampe.test.com:80/demo.js"  />
    
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    /* 通过 JS 文件动态创建 */ 
    // cross.js
    function addScript(src) {
      const script = document.createElement("script");
      script.setAttribute("type", "text/javascript");
      script.src = src;
      document.body.appendChild(script);
    }
    window.onload = function () {
      addScript("http:exampe.test.com:80/demo.js");
    };
    

CORS

  • 原理:HTTP 请求头可以设置跨域

  • 应用场景:访问白名单,公共访问接口

  • 实现方式setHeader 方法根据不同的请求库自行切换

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    // 设置哪个源可以访问我
    setHeader('Access-Control-Allow-Origin', '[*|url]')
    // 允许携带哪个头访问我
    setHeader('Access-Control-Allow-Headers', 'name')
    // 允许哪个方法访问我
    setHeader('Access-Control-Allow-Methods', 'PUT')
    // 允许携带cookie
    setHeader('Access-Control-Allow-Credentials', true)
    // 预检的存活时间
    setHeader('Access-Control-Max-Age', 6)
    // 允许返回的头
    setHeader('Access-Control-Expose-Headers', 'name')
    

window.postMessage

  • 应用场景:多窗口数据传递,iframe 数据传递

  • 实现方式

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    /* example.test.com:3001/index.js */
    window.postMessage("hello", "example.test.com:3002");
    window.onmessage = function (e) {
      console.log(e.data); // nice to meet you
    };
    
    /* example.test.com:3002/index.js */
    window.onmessage = function (e) {
      console.log(e.data); // hello
      e.source.postMessage("nice to meet you", e.origin);
    };
    

WebSocket

  • 原理:HTTP 请求经过桥接升级为 WS 长连接,此时客户端与服务器可以互相通信

  • 应用场景:聊天、直播、联机游戏等所有需要实时交互的场景

  • 实现方式

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    /* client */
    const socket = new WebSocket("ws://example.test.com:3002");
    window.postMessage("hello", "example.test.com:3002");
    socket.open = function () {
      socket.send("hello");
    };
    socket.onmessage = function (e) {
      console.log(e.data); // nice to meet you
    };
    
    /* service */
    // 监听 WS 请求
    

Node中间代理

  • 原理:同源策略限制在浏览器端,服务器向服务器请求不受同源策略限制
  • 应用场景:一般用于开发环境的本地代理,就是启动了一个本地服务器,向服务器请求资源

Nginx 反向代理

  • 原理:同 node 中间代理,实现方式一样,代理目标相反
  • 应用场景:一般用于前后端分离项目,生产环境的服务器代理
  • 与中间代理的差异:中间代理在客户端配置代理,将客户端代理到服务器;反向代理在服务器配置,将监听到的端口转发到目标服务
  • 点此了解更多 Nginx 内容
Licensed under CC BY-NC-SA 4.0