進階13-常見的跨域解決方案

  • 什么是同源策略

同源策略是指瀏覽器處于安全方面的考慮只允許本域下的接口交互。不同源的客戶端腳本在沒有明確授權的情況下,不能讀取對方的資源。
其中同源是指:
- 協議相同,比如http://lalala.comhttps://lalala.com不屬于同源
- 域名相同,比如http://b.lalala.comhttp://a.lalala.com不屬于同源
- 端口相同,比如http://lalala.com:8082http://lalala.com不屬于同源

  • 什么是跨域?跨域有幾種實現形式

跨域是指不同域之間的接口進行交互;
常見的跨域解決方式有如下幾種:
- ### JSONP
JSONP是JSON with Padding的簡寫,是應用JSON的一種方法,JSONP看起來 與JSON差不多,只不過是被包含在回調函數中的JSON。JSONP由兩部分組成,回調函數和JSON數據,回調函數是當響應到來時應該在頁面中調用的函數(這個函數會在頁面中提前定義好)。所以JSONP的原理是利用<script>標簽可以不受限制地從其他域加載資源的能力動態創建<script>標簽,然后在這個標簽中向指定url請求JSONP數據,當數據被服務端傳回頁面時,回調函數把JSON數據傳入并執行,從而達到從其他域直接訪問響應文本的目的。例如:
//事件處理函數,當需要去訪問其他域的文本時觸發 function eventHandler() { var script = document.createElement('script'); script.src = 'http://otherDomain.com/json/?callback=JSONHandler' document.body.appendChild(script); } //回調函數,當服務端傳回類似'JSONHandler' + '(' + json ')'的數據時調用 function JSONHandler(json) {}
下面是一個簡單通過JSONP實現的猜數例子:
客戶端代碼:
<!doctype html> <html> <head> </head> <body> <div id='ctn'> <input type="input" id="number" placeholder="1到100的整數" > <br> <button id="guess">猜吧</button> </div> <script> function $(str) { return document.querySelector(str); } var input = $('#number'); var guess = $('#guess'); //事件處理函數 guess.addEventListener( 'click', function() { var guessNumber = parseInt(input.value); if(isNaN(guessNumber)) { alert('老鐵,輸個數字過來好嗎');return;} var script = document.createElement('script'); script.src = 'http://localhost:8080/guessNumber?callback=xxx&number=' + guessNumber; document.body.appendChild(script); }) //回調函數 function xxx(json) { div = document.createElement('div'); div.innerText = '你猜的數字是:' + parseInt(input.value) + ',' + json[parseInt(input.value)]; ctn.appendChild(div); } </script> </body> </html>
服務端代碼:
var theNumber = 77; app.get('/guessNumber', function(req, res){ var data = {}; var guessNumber = parseInt(req.query.number); if(guessNumber > theNumber) { data[guessNumber] = '大了'; } else if(guessNumber < theNumber) { data[guessNumber] = '小了'; } else { data[guessNumber] = '中了'; } //返回JSONP var cb = req.query.callback; console.log(cb); if(cb) { let tmp = cb + '('+ JSON.stringify(data) +')' console.log(tmp); res.send(tmp); } else { res.send(data); } })
下圖為本地開啟server-mock的效果圖:

image.png

  • CROS

CROS(Cross-Origin Resource Sharing,跨域資源共享)是W3C定義的一個跨域資源共享規范,規定了在必須訪問跨域西原始瀏覽器與服務器應該如何溝通。整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。

比如一個簡單請求,它沒有自定義頭部,在發送之前需要給它附加一個額外的`Origin`頭部,其中包含請求頁面的源信息(協議、域名、端口),以便服務器根據這個頭部信息來決定是否給予響應;如果服務器認為該請求可以接受,則在響應報文的首部中添加`Access-Control-Allow-Origin`的頭,其值與請求報文中`Origin`頭的值一致,或者為`*`表示接受所有域的請求。這里的`Origin`頭的發送與是否跨域無關。
其中簡單請求的定義如下:
>只要同時滿足以下兩大條件,就屬于簡單請求
1.  請求方法是以下三種方法之一:
    - HEAD
    - GET
    - POST
2. HTTP的頭信息不超出以下幾種字段:
    - Accept
    - Accept-Language
    - Content-Language
    - Last-Event-ID

其中Content-Type:只限于三個值application/x-www-form-urlencoded、 multipart/form-data、text/plain

若是一個非簡單請求(相對簡單請求而言),則在正式發送請求報文之前需要通過`OPTIONS`方法發送一個預檢報文,報文首部有`Access-Control-request-method`頭和`Access-Control-request-Headers`頭,用以告知服務器實際請求所使用的HTTP方法和攜帶的自定義首部,然后服務器根據這些值判斷是否接受該請求;然后返回響應報文,在響應報文中添加`Access-Control-Allow-Methods`頭和`Access-Control-Allow-Headers`頭作為請求報文中對應頭的回復。
  • 降域

// 降域方式
// 修改document.domain的方法只適用于不同子域
<iframe id = "iframe" src="http://example.com/b.html" onload = "test()"></iframe>
<script type="text/javascript">
    document.domain = 'example.com';//設置成主域
    function test(){
        alert(document.getElementById('iframe').contentWindow);//contentWindow 可取得子窗口的 window 對象
    }
</script>
// 子窗口
<script type="text/javascript">
    document.domain = 'example.com';//在iframe載入這個頁面也設置document.domain,使之與主頁面的document.domain相同
</script>
  • postMessage

window.postMessage(message,targetOrigin)方法是html5新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。

調用`postMessage`方法的`window`對象是指要接收消息的那一個`window`對象,該方法的第一個參數`message`為要發送的消息,類型只能為字符串;第二個參數`targetOrigin`用來限定接收消息的那個`window`對象所在的域,如果不想限定域,可以使用通配符 *  。需要接收消息的`window`對象,可是通過監聽自身的`message`事件來獲取傳過來的消息,消息內容儲存在該事件對象的data屬性中。

更多請參看:
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#Preflighted_requests
http://zqdevres.qiniucdn.com/data/20160412110843/index.html
http://www.cnblogs.com/2050/p/3191744.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容