JavaScript 複習之 同源限制

所謂「同源」指的是「三個相同」:協議相同、域名相同及端口相同。javascript

非同源,共有三種行爲受到限制。java

  1. 沒法讀取非同源網頁的 Cookie、LocalStorage 和 IndexedDB。web

  2. 沒法接觸非同源網頁的 DOM。json

  3. 沒法向非同源地址發送 AJAX 請求(能夠發送,但瀏覽器會拒絕接受響應)。跨域

window.postMessage()

這個 API 爲window對象新增一個window.postMessage方法,容許跨窗口通訊,不論這倆個窗口是否同源。瀏覽器

// 父窗口打開一個子窗口
var popup = window.open('http://bbb.com', 'title');
// 父窗口向子窗口發消息
popup.postMessage('Hello World!', 'http://bbb.com');
複製代碼

方法接受兩個參數,第一個參數是具體的信息內容,第二個參數是接收消息的窗口的源,即「協議 + 域名 + 端口」。也能夠設爲*,表示不限制域名,向全部窗口發送。安全

子窗口向父窗口發送消息的寫法相似。bash

// 子窗口向父窗口發消息
window.opener.postMessage('Nice to see you', 'http://aaa.com');
複製代碼

父子窗口經過message事件,監聽對方的消息。服務器

// 父窗口和子窗口均可以用下面的代碼,
// 監聽 message 消息
window.addEventListener('message', function (e) {
  console.log(e.data);
},false);
複製代碼

message事件的參數是事件對象event,提供三個屬性websocket

  1. event.source:發送消息的窗口
  2. event.origin:消息發向的網址
  3. event.data:消息內容

下面的例子是,子窗口經過event.source屬性引用父窗口,而後發送消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  event.source.postMessage('Nice to see you!', '*');
}
複製代碼

上面代碼有幾個地方須要注意。首先,receiveMessage函數裏面沒有過濾信息的來源,任意網址發來的信息都會被處理。其次,postMessage方法中指定的目標窗口的網址是一個星號,表示該信息能夠向任意網址發送。一般來講,這兩種作法是不推薦的,由於不夠安全,可能會被惡意利用。

event.origin屬性能夠過濾不是發給本窗口的消息。

window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
  if (event.origin !== 'http://aaa.com') return;
  if (event.data === 'Hello World') {
    event.source.postMessage('Hello', event.origin);
  } else {
    console.log(event.data);
  }
}
複製代碼

AJAX

同源政策規定,AJAX 請求只能發給同源的網址,不然就報錯。

除了架設服務器代理(瀏覽器請求同源服務器,再由後者請求外部服務),有三種方法規避這個限制。

  • JSONP

  • WebSocket

  • CORS

jsonp

是服務器和客戶端跨源通訊的經常使用方法,特色就是簡單適用。

基本思想是,網頁經過添加一個<scritp>元素,向服務器請求 JSON 數據,這種作法不受同源策略限制;服務器收到請求後,將數據放在一個指定名字的回調函數裏傳回來。

首先,網頁動態插入<scritp>元素,由他向跨源網址發出請求。

function addScriptTag(src){
    let script = document.createElement('script');
    script.setAttribute('type','text/javascript');
    script.src = src;
    document.body.appendChild(script);
}

window.onload = function(){
    addScriptTag('http://example.com/ip?callback=foo');
}

function foo(data){
    console.log('Your public IP address is: ' + data.ip);
}
複製代碼

上面代碼經過動態添加<script>元素,向服務器example.com發出請求。注意,該請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對於 JSONP 是必需的。

服務器收到這個請求之後,會將數據放在回調函數的參數位置返回。

foo({
  "ip": "8.8.8.8"
});
複製代碼

WebSocket

是一種通訊協議,使用ws://(非加密)和wss://(加密)做爲協議前綴。該協議不實行同源策略,只要服務器支持,就能夠經過它進行跨源通訊。

下面是一個瀏覽器 發出的 WebStock 請求的頭部信息

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
複製代碼

上面代碼,有一個origin,表示請求的請求源,即發自哪一個域名。

正由於有了這個字段,因此 WebStock 纔沒有實行同源策略。由於服務器能夠根據這個字段,判斷是否許可本次通訊。若是該域名在白名單內,服務器就能夠作出以下回應

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
複製代碼

CORS

是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。屬於跨源 AJAX 請求的根本解決方法。容許任何類型的請求。

CORS 須要瀏覽器和服務器同時支持,目前全部瀏覽器都支持該功能。

實現 CORS 通訊的關鍵是服務器,只要服務器實現了 CORS 接口,就能夠跨域通訊。

相關文章
相關標籤/搜索