前端開發中咱們常常會遇到跨域請求的狀況,處理跨域請求方式不少大概分爲八種。html
提到跨域不能不先說一下」同源策略」。
何爲同源?只有當協議、端口、和域名都相同的頁面,則兩個頁面具備相同的源。只要網站的 協議名protocol、 主機host、 端口號port 這三個中的任意一個不一樣,網站間的數據請求與傳輸便構成了跨域調用,會受到同源策略的限制。
同源策略限制從一個源加載的文檔或腳本如何與來自另外一個源的資源進行交互。這是一個用於隔離潛在惡意文件的關鍵的安全機制。瀏覽器的同源策略,出於防範跨站腳本的攻擊,禁止客戶端腳本(如 JavaScript)對不一樣域的服務進行跨站調用(一般指使用XMLHttpRequest請求)。前端
特別注意兩點:
一、若是是協議和端口形成的跨域問題「前臺」是無能爲力的;
二、在跨域問題上,域僅僅是經過「URL的首部」來識別而不會去嘗試判斷相同的ip地址對應着兩個域或兩個域是否在同一個ip上。
URL的首部:指window.location.protocol +window.location.host,也能夠理解爲「Domains(域名), protocols(協議) and ports(端口) must match」。html5
解決跨域問題,最簡單的莫過於經過nginx反向代理進行實現,可是其須要在運維層面修改,且有可能請求的資源並再也不咱們控制範圍內(第三方),因此該方式不能做爲通用的解決方案,下面闡述了常常用到幾種跨域方式:nginx
圖片ping經常使用於跟蹤用戶點擊頁面或動態廣告曝光次數。
script標籤能夠獲得從其餘來源數據,這也是JSONP依賴的根據。
缺點:只能發送Get請求 ,沒法訪問服務器的響應文本(單向請求)跨域
JSONP(JSON with Padding)是數據格式JSON的一種「使用模式」,可讓網頁從別的網域要數據。根據 XmlHttpRequest 對象受到同源策略的影響,而利用 <script>元素的這個開放策略,網頁能夠獲得從其餘來源動態產生的JSON數據,而這種使用模式就是所謂的 JSONP。用JSONP抓到的數據並非JSON,而是任意的JavaScript,用 JavaScript解釋器運行而不是用JSON解析器解析。全部,經過Chrome查看全部JSONP發送的Get請求都是js類型,而非XHR。瀏覽器
缺點:安全
Cross-Origin Resource Sharing(CORS)跨域資源共享是一份瀏覽器技術的規範,提供了 Web 服務從不一樣域傳來沙盒腳本的方法,以避開瀏覽器的同源策略,確保安全的跨域數據傳輸。現代瀏覽器使用CORS在API容器如XMLHttpRequest來減小HTTP請求的風險來源。與 JSONP 不一樣,CORS 除了 GET 要求方法之外也支持其餘的 HTTP 要求。服務器通常須要增長以下響應頭的一種或幾種:服務器
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400
跨域請求默認不會攜帶Cookie信息,若是須要攜帶,請配置下述參數:app
"Access-Control-Allow-Credentials": true // Ajax設置 "withCredentials": true
window.name經過在iframe(通常動態建立i)中加載跨域HTML文件來起做用。而後,HTML文件將傳遞給請求者的字符串內容賦值給window.name。而後,請求者能夠檢索window.name值做爲響應。運維
iframe標籤的跨域能力;
window.name屬性值在文檔刷新後依舊存在的能力(且最大容許2M左右)。
每一個iframe都有包裹它的window,而這個window是top window的子窗口。contentWindow屬性返回<iframe>元素的Window對象。你可使用這個Window對象來訪問iframe的文檔及其內部DOM。
<!-- 下述用端口 10000表示:domainA 10001表示:domainB --> <!-- localhost:10000 --> <script> var iframe = document.createElement('iframe'); iframe.style.display = 'none'; // 隱藏 var state = 0; // 防止頁面無限刷新 iframe.onload = function() { if(state === 1) { console.log(JSON.parse(iframe.contentWindow.name)); // 清除建立的iframe iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; // 加載完成,指向當前域,防止錯誤(proxy.html爲空白頁面) // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame. iframe.contentWindow.location = 'http://localhost:10000/proxy.html'; } }; iframe.src = 'http://localhost:10001'; document.body.appendChild(iframe); </script> <!-- localhost:10001 --> <!DOCTYPE html> ... <script> window.name = JSON.stringify({a: 1, b: 2}); </script> </html>
直接嵌入其餘域(localhots:10001)下的URL會報錯,因此須要加載完成替換爲當前域的URL(localhots:10000),proxy.html爲空白頁面,只爲解決該問題;
從新設置src(http://localhost:10000/proxy.html)後致使頁面不斷刷新,因此經過state來控制;
所有獲取完結果後,清除該iframe。
window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源(可實現跨域),目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
message:爲要發送的消息,類型只能爲字符串;
targetOrigin:用來限定接收消息的那個window對象所在的域,若是不想限定域,可使用通配符 「*」。
1)建立www.test.com/a.html頁面代碼:
<script> function onLoad(){ var iframe = document.getElementById("iframe"); var win = iframe.contentWindow; win.postMessage('哈哈,我是來自頁面a.html的信息喲!','*');//向不一樣域的www.script.com/b.html發送消息 } </script> <iframe id="iframe" src="www.script.com/b.html" onload="onLoad()"></iframe>
2)建立www.script.com/b.html頁面代碼:
<script> window.onmessage = function(e){//註冊message時間來接收消息 e = e || event; //獲取時間對象 alert(e.data); //經過data屬性來獲得傳送的消息 } </script>
優勢:使用postMessage來跨域傳送數據仍是比較直觀和方便的;
缺點: IE六、IE7不支持,因此用不用還得根據實際須要來決定。
前提條件:這兩個域名必須屬於同一個基礎域名!並且所用的協議,端口都要一致,不然沒法利用document.domain進行跨域,因此只能跨子域
在根域範圍內,容許把domain屬性的值設置爲它的上一級域。例如,在」aaa.xxx.com」域內,能夠把domain設置爲 「xxx.com」 但不能設置爲 「xxx.org」 或者」com」。
如今存在兩個域名aaa.xxx.com和bbb.xxx.com。在aaa下嵌入bbb的頁面,因爲其document.name不一致,沒法在aaa下操做bbb的js。能夠在aaa和bbb下經過js將document.name = 'xxx.com';設置一致,來達到互相訪問的做用。
WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與服務器全雙工通訊,同時容許跨域通信,是server push技術的一種很棒的實現。相關文章,請查看:WebSocket、WebSocket-SockJS
須要注意:WebSocket對象不支持DOM 2級事件偵聽器,必須使用DOM 0級語法分別定義各個事件。
同源策略是針對瀏覽器端進行的限制,能夠經過服務器端來解決該問題
DomainA客戶端(瀏覽器) ==> DomainA服務器 ==> DomainB服務器 ==> DomainA客戶端(瀏覽器)
實現HTTP、HTTPS代理請參照: 建立HTTP與HTTPS服務器與客戶端