詳細梳理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.js
中 devServer
中如下設置即可:
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需要注意:
- 必須後端配置相應回調函數。
- 只能發送
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介面,那麼唯一的選擇就是使用自己的伺服器進行反向代理。
參考鏈接
本文完。
(啾咪 ^.<)