跨域

1.什么是同源策略

  • 瀏覽器出于安全方面的考慮,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對(duì)方的資源。

  • 同源指的是:

    • 同協(xié)議
    • 同域名
    • 同端口
  • 作用:保證用戶信息的安全,防止惡意的網(wǎng)站竊取數(shù)據(jù)

    • 例1:A網(wǎng)站是一家銀行,用戶登錄以后,又去瀏覽其他網(wǎng)站。如果其他網(wǎng)站可以讀取A網(wǎng)站的 Cookie,會(huì)發(fā)生什么?很顯然,如果 Cookie 包含隱私(比如存款總額),這些信息就會(huì)泄漏。除此之外,Cookie 往往用來(lái)保存用戶的登錄狀態(tài),如果用戶沒有退出登錄,其他網(wǎng)站就可以冒充用戶,為所欲為。(因?yàn)闉g覽器同時(shí)還規(guī)定,提交表單不受同源政策的限制)
    • 例2:惡意網(wǎng)站的頁(yè)面通過(guò)iframe嵌入了銀行的登錄頁(yè)面(二者不同源),如果沒有同源限制,惡意網(wǎng)頁(yè)上的javascript腳本就可以在用戶登錄銀行的時(shí)候獲取用戶名和密碼,從而造成相關(guān)的風(fēng)險(xiǎn)。
  • 對(duì)于當(dāng)前頁(yè)面來(lái)說(shuō)頁(yè)面中 JS 文件的域不重要,重要的是當(dāng)前頁(yè)面所在的域與 腳本中涉及到的域(例如xht的open方法的url)是否同源

2.什么是跨域?跨域有幾種實(shí)現(xiàn)形式

  • 跨域:瀏覽器出于安全方面的考慮設(shè)置了同源策略來(lái)限制不同源之間的交互,但是也阻礙了不同域之間的協(xié)助。為了實(shí)現(xiàn)不同源之間的交互、協(xié)作,因此需要“跨域”。

  • 跨域的幾種實(shí)現(xiàn)形式:

    • JSONP(填充式JSON或參數(shù)式JSON)
    • CORS(跨源資源共享)
    • 降域
    • postMessage

3.JSONP的原理是什么?

  • 基本思想是,網(wǎng)頁(yè)通過(guò)添加一個(gè)<script>元素,向服務(wù)器請(qǐng)求JSON數(shù)據(jù),這種做法不受同源政策限制;服務(wù)器收到請(qǐng)求后,將數(shù)據(jù)放在一個(gè)指定名字的回調(diào)函數(shù)里傳回來(lái)。傳回后回調(diào)函數(shù)立即執(zhí)行(參數(shù)是后端產(chǎn)生的數(shù)據(jù)),從而實(shí)現(xiàn)相應(yīng)的功能。
  • 具體流程:
    1.網(wǎng)頁(yè)動(dòng)態(tài)地插入<script>元素,由它向跨域網(wǎng)址發(fā)出請(qǐng)求
function addScriptTag(src){
    var script = document.createElement('script');
    script.src = src; //跨域網(wǎng)址
    document.body.appendChild(script);  
    // 往頁(yè)面插入元素后,會(huì)向跨域網(wǎng)址發(fā)出請(qǐng)求(src指定了跨域網(wǎng)址,得到響應(yīng)后立即執(zhí)行
}

window.onload = function(){
    addScriptTag("http://example.com/ip?callback = foo');  //當(dāng)頁(yè)面加載完畢,即往頁(yè)面中插入script元素
}

function foo(data){
    console.log('your public ip address is: '+ data.ip)'
}

2.上面代碼通過(guò)動(dòng)態(tài)添加<script>元素,向服務(wù)器example.com發(fā)出請(qǐng)求。注意,該請(qǐng)求的查詢字符串有一個(gè)callback參數(shù),用來(lái)指定回調(diào)函數(shù)的名字,這對(duì)于JSONP是必需的。
3.服務(wù)器收到該請(qǐng)求后,會(huì)將數(shù)據(jù)放在回到函數(shù)的參數(shù)位置返回

foo({
  "ip": "8.8.8.8"
});

4.由于<script>元素請(qǐng)求的腳本,直接作為代碼運(yùn)行。這時(shí),只要瀏覽器定義了foo函數(shù),該函數(shù)就會(huì)立即調(diào)用。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對(duì)象,而不是字符串,因此避免了使用JSON.parse的步驟。

  • 注意:JSONP只能發(fā) GET 請(qǐng)求(因?yàn)檎?qǐng)求是放在<script>的scr中的)。

4.CORS是什么?

  • CORS: Cross-Origin Resource Sharing, 跨源資源共享,是W3C的一個(gè)工作草案,定義了在必須訪問跨源資源時(shí),瀏覽器與服務(wù)器應(yīng)該如何溝通,它是一種 ajax跨域請(qǐng)求資源的方式,支持現(xiàn)代瀏覽器,IE支持10以上。

  • 實(shí)現(xiàn)過(guò)程:當(dāng)使用 XMLHttpRequest發(fā)送請(qǐng)求時(shí),瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略,會(huì)自動(dòng)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin,并將請(qǐng)求發(fā)送。服務(wù)器端收到請(qǐng)求后,如果確定接受請(qǐng)求則在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin; 瀏覽器收到響應(yīng)后判斷該相應(yīng)頭中是否包含 Origin 的值,如果有則瀏覽器會(huì)處理響應(yīng),我們就可以拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回,這時(shí)我們無(wú)法拿到響應(yīng)數(shù)據(jù)。

  • 整個(gè)CORS通信過(guò)程,都是瀏覽器自動(dòng)完成,不需要用戶參與

  • 實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信。

  • 詳細(xì)流程:
    1.瀏覽器發(fā)現(xiàn)這次請(qǐng)求不符合同源策略,就自動(dòng)在頭信息之中,添加一個(gè)Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

在上面的頭信息中,Origin字段表明了本次請(qǐng)求來(lái)自哪個(gè)源:協(xié)議、域名、端口。

2.該請(qǐng)求到達(dá)服務(wù)器后,服務(wù)器會(huì)根據(jù)這個(gè)值來(lái)判斷是否接受請(qǐng)求。如果Origin指定的域名在許可范圍內(nèi),服務(wù)器返回的響應(yīng),會(huì)多出幾個(gè)頭信息字段(如下所示)。如果Origin指定的源,不在許可范圍內(nèi),服務(wù)器會(huì)返回一個(gè)正常的HTTP回應(yīng)。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

3.瀏覽器收到響應(yīng)后判斷該相應(yīng)頭中是否包含Origin的值,如果響應(yīng)的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯(cuò)了,從而拋出一個(gè)錯(cuò)誤,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲,這時(shí)我們無(wú)法拿到響應(yīng)數(shù)據(jù)。如果有則瀏覽器會(huì)處理響應(yīng),我們就可以拿到響應(yīng)數(shù)據(jù)。

5.降域

  • 降域獲取同一Cookie:Cookie 是服務(wù)器寫入瀏覽器的一小段信息,只有同源的網(wǎng)頁(yè)才能共享。但是,兩個(gè)網(wǎng)頁(yè)一級(jí)域名相同,只是二級(jí)域名不同,瀏覽器允許通過(guò)設(shè)置document.domain共享 Cookie。
// A網(wǎng)頁(yè)和B網(wǎng)頁(yè)設(shè)置相同的document.domain
document.domain = 'example.com'
// A網(wǎng)頁(yè)通過(guò)腳本設(shè)置Cookie
document.cookie = "test1 = hello";
// B網(wǎng)頁(yè)可以獲取到該cookie
var otherCookie = document.cookie;
  • 降域使不同源的iframe窗口和父窗口相互通信:如果兩個(gè)網(wǎng)頁(yè)不同源,就無(wú)法拿到對(duì)方的DOM。典型的例子是iframe窗口和與父窗口無(wú)法通信。如果兩個(gè)窗口一級(jí)域名相同,只是二級(jí)域名不同,那么設(shè)置document.domain屬性,就可以規(guī)避同源政策,拿到DOM。
A 網(wǎng)頁(yè)
URL: http://a.yanxin.com:8080/a.html
<div class="ct">
  <h1>使用降域?qū)崿F(xiàn)跨域</h1>
  <div class="main">
    <input type="text" placeholder="http://a.yanxin.cn:8080/a.html">
  </div>
  <iframe src="http://b.yanxin.com:8080/b.html" frameborder="0" ></iframe>
</div>

<script>

document.querySelector('.main input').addEventListener('input', function(){
  console.log(this.value);
  /* window.frames 是窗口中所有命名的框架組成的數(shù)組。
  這個(gè)數(shù)組的每個(gè)元素都是一個(gè)Window對(duì)象,對(duì)應(yīng)于窗口中的一個(gè)框架。
  window.frames[0]得到的就是html中的框架
  window.frames[0].document.querySelector('input') 得到框架中的input元素
  */
  window.frames[0].document.querySelector('input').value = this.value;
})

document.domain = "yanxin.com"
</script>
iframe中的B網(wǎng)頁(yè)
URL: http://b.yanxin.com:8080/b.html
<input id="input" type="text"  placeholder="http://b.yanxin.com:8080/b.html">
<script>

document.querySelector('#input').addEventListener('input', function(){
    // 得到父窗口中的input元素
    window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'yanxin.com';
</script>

6.postMessage

  • HTML5為了解決跨域問題,引入了一個(gè)全新的API:跨文檔通信 API(Cross-document messaging)。
    這個(gè)API為window對(duì)象新增了一個(gè)window.postMessage方法,允許跨窗口通信,不論這兩個(gè)窗口是否同源。

  • 目的:向另一個(gè)地方傳遞數(shù)據(jù),另一個(gè)地方指的是:包含在當(dāng)前頁(yè)面的<iframe>元素,或者由當(dāng)前頁(yè)面彈出的窗口

  • window.postMessage() 方法被調(diào)用時(shí),會(huì)在所有頁(yè)面腳本執(zhí)行完畢之后向目標(biāo)窗口派發(fā)一個(gè) MessageEvent 消息。 * otherWindow.postMessage(message, targetOrigin, [transfer]);

    • otherWindow:其他窗口的一個(gè)引用(相對(duì)于當(dāng)前的窗口的其他窗口),比如iframe的contentWindow屬性、執(zhí)行window.open返回的窗口對(duì)象、或者是命名過(guò)或數(shù)值索引的window.frames。
    • message將要發(fā)送到其他 window的數(shù)據(jù)
    • targetOrigin:指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無(wú)限制)或者一個(gè)URI。在發(fā)送消息的時(shí)候,如果目標(biāo)窗口的協(xié)議、主機(jī)地址或端口這三者的任意一項(xiàng)不匹配targetOrigin提供的值,那么消息就不會(huì)被發(fā)送;只有三者完全匹配,消息才會(huì)被發(fā)送。
    • 注意:如果你明確的知道消息應(yīng)該發(fā)送到哪個(gè)窗口,那么請(qǐng)始終提供一個(gè)有確切值的targetOrigin,而不是*。不提供確切的目標(biāo)將導(dǎo)致數(shù)據(jù)泄露到任何對(duì)數(shù)據(jù)感興趣的惡意站點(diǎn)。
  • 示例

頁(yè)面A:  http://a.yanxin.cn:8080/a.html
在頁(yè)面A中打開頁(yè)面B: http://b.yanxin.cn:8080/b.html
當(dāng)點(diǎn)擊頁(yè)面A上的button時(shí),向頁(yè)面B傳輸消息"hello world"
  <button id="sendmessage">send message</button>
  <script>
   var button = document.querySelector("#sendmessage");
  // 打開頁(yè)面b,并且取得對(duì)它的引用
   var targetWindow = window.open("http://b.yanxin.cn:8080/b.html");
   button.addEventListener('click',function(){
   // 注意: 這里是向targetWindow即新打開的窗口(通過(guò)上面的window.open打開的窗口)發(fā)送消息
  // 直接打開頁(yè)面b(手動(dòng)打開的)是無(wú)法收到消息的
  // 如果直接調(diào)用postMessage則相當(dāng)于當(dāng)前窗口向自己發(fā)送消息
      targetWindow.postMessage("hello world!!", "http://b.yanxin.cn:8080/b.html");
   });
   
  </script>
為頁(yè)面B的window添加監(jiān)聽器
當(dāng)頁(yè)面B收到Message時(shí),在控制臺(tái)中輸出收到的消息,并提示"hello"
<script>
    window.addEventListener("message", receiveMessage);
    function receiveMessage(event){
        console.log(event.data);
        alert("hello");
    }
</script>

參考資料:

CORS 詳解-阮一峰
瀏覽器同源政策及其規(guī)避方法-阮一峰
Window.postMessage() - Web APIs | MDN

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 跨域問題的場(chǎng)景和解決方案多種多樣,只要是做前端開發(fā),總會(huì)遇到。而且面試時(shí)也是必問的問題。所以自己學(xué)習(xí)總結(jié)記錄一下。...
    花開_陳鳳娟閱讀 783評(píng)論 0 0
  • 歡迎關(guān)注微信公眾號(hào):全棧工廠 本文主要參考跨域資源共享 CORS 詳解[http://www.ruanyifeng...
    liqingbiubiu閱讀 2,051評(píng)論 0 3
  • 一、瀏覽器的同源策略 1.什么是同源? 所謂“同源”指的是”三個(gè)相同“。相同的域名、端口和協(xié)議,這三個(gè)相同的話就視...
    徐國(guó)軍_plus閱讀 926評(píng)論 1 3
  • 什么是同源策略 瀏覽器出于安全方面的考慮,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能...
    ezrealor閱讀 560評(píng)論 0 1
  • 因?yàn)槌跞龥]有考上高中,只能聽父母的話來(lái)到了現(xiàn)在就讀的學(xué)校,來(lái)到這個(gè)學(xué)校最幸福的事就是遇到他,最初對(duì)他的了解還是...
    Bunuan閱讀 181評(píng)論 0 0

友情鏈接更多精彩內(nèi)容