前端面試查漏補缺--(三) 跨域及常見解決辦法

shotCat發表於2019-02-22

前言

本系列最開始是為了自己面試準備的.後來發現整理越來越多,差不多有十二萬字元,最後決定還是分享出來給大家.

為了分享整理出來,花費了自己大量的時間,起碼是隻自己用的三倍時間.如果喜歡的話,歡迎收藏,關注我!謝謝!

文章連結

合集篇:

前端面試查漏補缺--Index篇(12萬字元合集) 包含目前已寫好的系列其他十幾篇文章.後續新增值文章不會再在每篇新增連結,強烈建議議點贊,關注合集篇!!!!,謝謝!~

後續更新計劃

後續還會繼續新增設計模式,前端工程化,專案流程,部署,閉環,vue常考知識點 等內容.如果覺得內容不錯的話歡迎收藏,關注我!謝謝!

求一份內推

目前本人也在準備跳槽,希望各位大佬和HR小姐姐可以內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~

概述

因為瀏覽器出於安全考慮,有了同源策略,這樣就導致只要協議、域名、埠有任何一個不同,都被當作是不同的域。也就是說,如果協議、域名或者埠有一個不同就是跨域,Ajax 請求就會失敗。

常見跨域的解決辦法

JSONP

JSONP 的原理很簡單,就是利用 <script> 標籤沒有跨域限制的漏洞。通過 <script> 標籤指向一個需要訪問的地址並提供一個回撥函式來接收資料當需要通訊時。

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
    	console.log(data)
	}
</script>

複製程式碼

JSONP 使用簡單且相容性不錯,但是只限於 get 請求。

在開發中可能會遇到多個 JSONP 請求的回撥函式名是相同的,這時候就需要自己封裝一個 JSONP,以下是簡單實現

function jsonp(url, jsonpCallback, success) {
  let script = document.createElement('script')
  script.src = url
  script.async = true
  script.type = 'text/javascript'
  window[jsonpCallback] = function(data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
  console.log(value)
})
複製程式碼

CORS

CORS(Cross-Origin ResourceSharing)跨域資源共享,定義了必須在訪問跨域資源時,瀏覽器與伺服器應該如何溝通。

CORS背後的基本思想就是使用自定義的HTTP頭部讓瀏覽器與伺服器進行溝通,從而決定請求或響應是應該成功還是失敗。 目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10。 因為需要通過 XDomainRequest 來實現。

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

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

伺服器端對於CORS的支援,主要就是通過設定Access-Control-Allow-Origin來進行的。該屬性表示哪些域名可以訪問資源,如果設定萬用字元則表示所有網站都可以訪問資源。如果瀏覽器檢測到相應的設定,就可以允許Ajax進行跨域的訪問。

postMessage

這是由H5提出來的的API,IE8以上支援這個功能。window.postMessage() 方法可以安全地實現跨源通訊。通常,對於兩個不同頁面的指令碼,只有當執行它們的頁面位於具有相同的協議(通常為https),埠號(443為https的預設值),以及主機 (兩個頁面的模數 Document.domain設定為相同的值) 時,這兩個指令碼才能相互通訊。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。

window.postMessage() 方法被呼叫時,會在所有頁面指令碼執行完畢之後,向目標視窗派發一個 MessageEvent 訊息。

該MessageEvent訊息有四個屬性需要注意:

  • message 屬性表示該message 的型別;
  • data 屬性為 window.postMessage 的第一個引數;
  • origin 屬性表示呼叫window.postMessage() 方法時呼叫頁面的當前狀態;
  • source 屬性記錄呼叫 window.postMessage() 方法的視窗資訊。

語法

otherWindow.postMessage(message, targetOrigin);

  • otherWindow:指目標視窗,也就是給哪個window發訊息,是 window.frames 屬性的成員或者由 window.open 方法建立的視窗
  • message: 是要傳送的訊息,型別為 String、Object (IE8、9 不支援)
  • targetOrigin: 是限定訊息接收範圍,不限制請使用 '*

例子:

A頁面通過postMessage方法傳送訊息:

window.onload = function() {  
    var ifr = document.getElementById('ifr');  
    var targetOrigin = "http://www.google.com";  
    ifr.contentWindow.postMessage('hello world!', targetOrigin);  
};
複製程式碼

B頁面通過message事件監聽並接受訊息:

var onmessage = function (event) {  
  var data = event.data;//訊息  
  var origin = event.origin;//訊息來源地址  
  var source = event.source;//源Window物件  
  if(origin=="http://www.baidu.com"){  
console.log(data);//hello world!  
  }  
};  
if (typeof window.addEventListener != 'undefined') {  
  window.addEventListener('message', onmessage, false);  
} else if (typeof window.attachEvent != 'undefined') {  
  //for ie  
  window.attachEvent('onmessage', onmessage);  
}
複製程式碼

nginx代理跨域

跨域原理: 同源策略是瀏覽器的安全策略,不是HTTP協議的一部分。伺服器端呼叫HTTP介面只是使用HTTP協議,不會執行JS指令碼,不需要同源策略,也就不存在跨越問題。

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

nginx具體配置:

#proxy伺服器
server {
    listen       81;
    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;
    }
}
複製程式碼

WebSocket與NodeJs中介軟體跨域等其他方法

這些方法除了nodejs用得都不是特別多,WebSocket不會專門用來做跨域,而是作為訊息推送或者聊天等.

其餘的方法,詳細可以參考: 前端常見跨域解決方案(全)

相關文章