詳細梳理ajax跨域4種解決方案

  • 2019 年 11 月 7 日
  • 筆記

前言

自動接觸前端,跨域這個詞就一直縈繞在耳畔。因為一般接手的項目都已經做好了這方面的處理,而且之前一直感覺對這方面模稜兩可,所以今天就抽個時間梳理一下。

為什麼需要跨域

跨域這個概念來自一個叫 「同源策略」 的東西。同源策略是瀏覽器(注意是瀏覽器,跟通訊協議無關)上為了安全考慮實施的非常重要的安全機制。

Ajax 默認只能獲取到同源的數據,對於非同源的數據,Ajax是獲取不到的。

什麼是同源?

  • 協議相同
  • 域名相同
  • 埠相同

舉例來說,http://www.example.com/dir/page.html這個網址,協議是http://,域名是www.example.com,埠是80(默認埠可以省略)。這個網址,在這個地址中要去訪問下面伺服器的數據,那麼會發生什麼情況呢?

URL 結果 原因
https://www.example.com/dir/other.html 不同源 協議不同,https 和 http
http://en.example.com/dir/other.html 不同源 域名不同
http://www.example.com:81/dir/other.html 不同源 埠不同
http://www.example.com/dir/page2.html 同源 協議,域名,埠都相同
http://www.example.com/dir2/page.html 同源 協議,域名,埠都相同

那麼。想要獲取非同源地址的數據,就要使用跨域。不論是 Ajax 還是跨域,都是為了訪問伺服器的數據。簡單的來說, Ajax 是為了訪問自己伺服器的數據,跨域是為了訪問別人伺服器的數據(比如獲取天氣資訊,航班資訊等)。

同源策略的目的

為了保證用戶資訊的安全,防止惡意的網站竊取數據。

「 設想這樣一種情況:A網站是一家銀行,用戶登錄以後,又去瀏覽其他網站。如果其他網站可以讀取A網站的 Cookie,會發生什麼?

很顯然,如果 Cookie 包含隱私(比如存款總額),這些資訊就會泄漏。更可怕的是,Cookie 往往用來保存用戶的登錄狀態,如果用戶沒有退出登錄,其他網站就可以冒充用戶,為所欲為。因為瀏覽器同時還規定,提交表單不受同源政策的限制。

由此可見,"同源政策"是必需的,否則 Cookie 可以共享,互聯網就毫無安全可言了。」

via@ 阮一峰

實現跨域的方式

  • 反向代理
  • JSONP
  • WebSocket
  • CORS(根本解決方案)

反向代理

反向代理就是使用自己的伺服器,在後端請求目標伺服器的數據,然後返回給客戶端。相當於做了一把中間人的感覺。

反向代理伺服器,最常用的就是Nginx。但是作為前端程式碼實現的Node.js也可以搭建反向代理伺服器。

下面來簡要介紹使用node服務進行反向代理。

要實現這個前提是,前端開發環境必須運行在nodejs服務中,所幸的是,現在前端的開發自動化工具都是建立在nodejs上的,所以這個前提也不是很重要。

比如我有一個後端介面:http://39.105.136.190:3000/zhuiszhu/goods/getList,可以獲取一些商品列表數據,但是我運行的node項目是在 localhost:3000 下的,明顯構成跨域。

我們根據項目使用的框架不同,處理的方式也不同。

1、nodejs+express+http-proxy-middleware 插件代理

如果是express項目,可以使用http-proxy-middleware 來處理,這個插件主要用於將前端請求代理到其它伺服器。

用法很簡單。你可以參考插件github官網: https://github.com/chimurai/http-proxy-middleware

首先需要在你的express項目中安裝該插件:

npm install --save-dev http-proxy-middleware

然後在 app.js 中進行代理設置(示例如下):

var express = require('express');  var proxy = require('http-proxy-middleware');    var app = express();    app.use('/zhuiszhu', proxy({target: 'http://39.105.136.190:3000/', changeOrigin: true}));  app.listen(3000); 

之後再項目中再次發送ajax請求的時候,可以看到數據可以收到了。

$.ajax({      url: '/zhuiszhu/goods/getList',      type: 'GET',      success(res) {},  });

2、webpack-dev-server 代理

我們經常在vue開發項目的時候,會用webpack作為前端自動化構建工具的話,也會使用到webpack-dev-server的插件,那麼可以引用webpack-dev-server來進行配置跨域方案。

webpack-dev-server是一個小型的nodejs伺服器,是基於express框架的,用於實時監聽和打包編譯靜態資源。其中裡面有一個屬性是proxy,是專門來配置代理請求介面的。

你只需要在webpack.config.jsdevServer中如下設置即可:

devServer: {          port: 3000,          inline: true,          proxy: {              "/zhuiszhu": {                  target: "http://39.105.136.190:3000/",                  changeOrigin: true  //必須配置為true,才能正確代理              }          }      },

其實可以看出, webpack 的 devServer.proxy 就是使用了非常強大的 http-proxy-middleware 包來實現代理的,所以本質上是相通的。

JSONP

JSONP基本思想是,網頁通過添加一個<script>元素,向伺服器請求JSON數據,這種做法不受同源政策限制;伺服器收到請求後,將數據作為參數放在一個指定名字的回調函數里傳回來,這個回調函數的名字我們需要通過js定義好。

比如:當頁面資源載入完畢時候,獲取跨域的數據,並且制定回調函數的名字為foo

window.onload = function () {      var script = document.createElement("script");      script.setAttribute("type","text/javascript");      script.src = "http://bbb.com?callback=foo;      document.body.appendChild(script);  };    foo(data) {      console.log(data); // data即為跨域獲取到的數據  }

在伺服器那邊,需要將數據放入foo函數的參數中:

foo('hello world')

使用JSONP需要注意:

  1. 必須後端配置相應回調函數。
  2. 只能發送GET請求。

WebSocket

WebSocket是一種通訊協議,使用ws://(非加密)和wss://(加密)作為協議前綴。該協議不實行同源政策,只要伺服器支援,就可以通過它進行跨源通訊。

由於發出的WebSocket請求中有有一個欄位是Origin,表示該請求的請求源(origin),即發自哪個域名。

正是因為有了Origin這個欄位,所以WebSocket才沒有實行同源政策。因為伺服器可以根據這個欄位,判斷是否許可本次通訊。

CORS

CORS全稱「 Cross-origin resource sharing 」(跨域資源共享),相比JSONP, CORS允許任何類型的請求 。

CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。

整個CORS通訊過程,都是瀏覽器自動完成,不需要用戶參與。對於開發者來說,CORS通訊與同源的AJAX通訊沒有差別,程式碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭資訊,有時還會多出一次附加的請求,但用戶不會有感覺。

因此,實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊。

總結

綜上,如果訪問的別人伺服器的資源,並且未設置JSONP,也未開放WebSocket白名單,也沒有設置CORS介面,那麼唯一的選擇就是使用自己的伺服器進行反向代理。

參考鏈接

瀏覽器同源政策及其規避方法

跨域資源共享 CORS 詳解

express框架介紹

http-proxy-middleware

本文完。

(啾咪 ^.<)