九種跨域方式實現原理(完整版)

前言

前後端資料互動經常會碰到請求跨域,什麼是跨域,以及有哪幾種跨域方式,這是本文要探討的內容。

一、什麼是跨域?

1。什麼是同源策略及其限制內容?

同源策略是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSRF等攻擊。所謂同源是指“協議+域名+埠”三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。

九種跨域方式實現原理(完整版)

同源策略限制內容有:

Cookie、LocalStorage、IndexedDB 等儲存性內容

DOM 節點

AJAX 請求傳送後,結果被瀏覽器攔截了

但是有三個標籤是允許跨域載入資源:

九種跨域方式實現原理(完整版)

// b。html window。onmessage = function(e) { console。log(e。data) //我愛你 e。source。postMessage(‘我不愛你’, e。origin) }

4。websocket

Websocket是HTML5的一個持久化的協議,它實現了瀏覽器與伺服器的全雙工通訊,同時也是跨域的一種解決方案。WebSocket和HTTP都是應用層協議,都基於 TCP 協議。

但是 WebSocket 是一種雙向通訊協議,在建立連線之後,WebSocket 的 server 與 client 都能主動向對方傳送或接收資料。

同時,WebSocket 在建立連線時需要藉助 HTTP 協議,連線建立好了之後 client 與 server 之間的雙向通訊就與 HTTP 無關了。

原生WebSocket API使用起來不太方便,我們使用

Socket。io

,它很好地封裝了webSocket介面,提供了更簡單、靈活的介面,也對不支援webSocket的瀏覽器提供了向下相容。

我們先來看個例子:本地檔案socket。html向

localhost:3000

發生資料和接受資料

// socket。html

// server。jslet express = require(‘express’);let app = express();let WebSocket = require(‘ws’);//記得安裝wslet wss = new WebSocket。Server({port:3000});wss。on(‘connection’,function(ws) { ws。on(‘message’, function (data) { console。log(data); ws。send(‘我不愛你’) });})

5。 Node中介軟體代理(兩次跨域)

實現原理:

同源策略是瀏覽器需要遵循的標準,而如果是伺服器向伺服器請求就無需遵循同源策略。

代理伺服器,需要做以下幾個步驟:

接受客戶端請求 。

將請求 轉發給伺服器。

拿到伺服器 響應 資料。

將 響應 轉發給客戶端。

九種跨域方式實現原理(完整版)

我們先來看個例子:本地檔案index。html檔案,透過代理伺服器

http://localhost:3000

向目標伺服器

http://localhost:4000

請求資料。

// index。html(http://127。0。0。1:5500)

// server1。js 代理伺服器(http://localhost:3000)const http = require(‘http’)// 第一步:接受客戶端請求const server = http。createServer((request, response) => {// 代理伺服器,直接和瀏覽器直接互動,需要設定CORS 的首部欄位 response。writeHead(200, { ‘Access-Control-Allow-Origin’: ‘*’, ‘Access-Control-Allow-Methods’: ‘*’, ‘Access-Control-Allow-Headers’: ‘Content-Type’ }) // 第二步:將請求轉發給伺服器 const proxyRequest = http 。request( { host: ‘127。0。0。1’, port: 4000, url: ‘/’, method: request。method, headers: request。headers }, serverResponse => { // 第三步:收到伺服器的響應 var body = ‘’ serverResponse。on(‘data’, chunk => { body += chunk }) serverResponse。on(‘end’, () => { console。log(‘The data is ’ + body) // 第四步:將響應結果轉發給瀏覽器 response。end(body) }) } ) 。end()})server。listen(3000, () => {console。log(‘The proxyServer is running at http://localhost:3000’)})

// server2。js(http://localhost:4000)const http = require(‘http’)const data = { title: ‘fontend’, password: ‘123456’ }const server = http。createServer((request, response) => { if (request。url === ‘/’) { response。end(JSON。stringify(data)) }})server。listen(4000, () => { console。log(‘The server is running at http://localhost:4000’)})

上述程式碼經過兩次跨域,值得注意的是瀏覽器向代理伺服器傳送請求,也遵循同源策略,最後在index。html檔案打印出

{“title”:“fontend”,“password”:“123456”}

6。nginx反向代理

實現原理類似於Node中介軟體代理,需要你搭建一箇中轉nginx伺服器,用於轉發請求。

使用nginx反向代理實現跨域,是最簡單的跨域方式。只需要修改nginx的配置即可解決跨域問題,支援所有瀏覽器,支援session,不需要修改任何程式碼,並且不會影響伺服器效能。

實現思路:透過nginx配置一個代理伺服器(域名與domain1相同,埠不同)做跳板機,反向代理訪問domain2介面,並且可以順便修改cookie中domain資訊,方便當前域cookie寫入,實現跨域登入。

先下載nginx,然後將nginx目錄下的nginx。conf修改如下:

// proxy伺服器server { listen 80; server_name www。domain1。com; location /{ proxy_pass http://www。domain2。com:8080; #反向代理 proxy_cookie_domain www。domain2。com www。domain1。com; #修改cookie裡域名 index index。html index。htm; # 當用webpack-dev-server等中介軟體代理介面訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啟用 add_header Access-Control-Allow-Origin http://www。domain1。com; #當前端只跨域不帶cookie時,可為* add_header Access-Control-Allow-Credentials true; }}

最後透過命令列

nginx -s reload

啟動nginx

// index。htmlvar xhr = new XMLHttpRequest();// 前端開關:瀏覽器是否讀寫cookiexhr。withCredentials = true;// 訪問nginx中的代理伺服器xhr。open(‘get’, ‘http://www。domain1。com:81/?user=admin’, true);xhr。send();

// server。jsvar http = require(‘http’);var server = http。createServer();var qs = require(‘querystring’);server。on(‘request’, function(req, res) { var params = qs。parse(req。url。substring(2)); // 向前臺寫cookie res。writeHead(200, { ‘Set-Cookie’: ‘l=a123456;Path=/;Domain=www。domain2。com;HttpOnly’ // HttpOnly:指令碼無法讀取 }); res。write(JSON。stringify(params)); res。end(); });server。listen(‘8080’);console。log(‘Server is running at port 8080。。。’);

7。window。name + iframe

window。name屬性的獨特之處:name值在不同的頁面(甚至不同域名)載入後依舊存在,並且可以支援非常長的 name 值(2MB)。

其中a。html和b。html是同域的,都是

http://localhost:3000;

而c。html是

http://localhost:4000

// a。html(http://localhost:3000/b。html)

b。html為中間代理頁,與a。html同域,內容為空。

// c。html(http://localhost:4000/c。html)

總結:透過iframe的src屬性由外域轉向本地域,跨域資料即由iframe的window。name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操作。

8。location。hash + iframe

實現原理: a。html欲與c。html跨域相互通訊,透過中間頁b。html來實現。 三個頁面,不同域之間利用iframe的location。hash傳值,相同域之間直接js訪問來通訊。

具體實現步驟:一開始a。html給c。html傳一個hash值,然後c。html收到hash值後,再把hash值傳遞給b。html,最後b。html將結果放到a。html的hash值中。

同樣的,a。html和b。html是同域的,都是

http://localhost:3000;

而c。html

是http://localhost:4000

// a。html

// b。html

// c。html console。log(location。hash); let iframe = document。createElement(‘iframe’); iframe。src = ‘http://localhost:3000/b。html#idontloveyou’; document。body。appendChild(iframe);

9。document。domain + iframe

該方式只能用於二級域名相同的情況下,比如

a.test.com

b.test.com

適用於該方式。

只需要給頁面新增

document。domain =‘test。com’

表示二級域名都相同就可以實現跨域。

實現原理:兩個頁面都透過js強制設定document。domain為基礎主域,就實現了同域。

我們看個例子:頁面

a。zf1。cn:3000/a。html

獲取頁面

b。zf1。cn:3000/b。html

中a的值

// a。html helloa

// b。html hellob

三、總結

CORS支援所有型別的HTTP請求,是跨域HTTP請求的根本解決方案

JSONP只支援GET請求,JSONP的優勢在於支援老式瀏覽器,以及可以向不支援CORS的網站請求資料。

不管是Node中介軟體代理還是nginx反向代理,主要是透過同源策略對伺服器不加限制。

日常工作中,用得比較多的跨域方案是cors和nginx反向代理

轉自簡書:前端三少爺

原文連結:https://www。jianshu。com/p/2896288494df