瀏覽器同源政策以及JS跨域

瀏覽器同源政策以及JS跨域

同源是指協議相同、域名相同、端口相同。同源政策的目的,是爲了保證用戶信息的安全,防止惡意的網站竊取數據。javascript

同源策略主要限制下面三種狀況html

  • Cookie 沒法讀取java

  • DOM 沒法得到web

  • AJAX 請求不能發送json

同源策略的本意是爲了保證用戶的信息安全。但有時也會帶來不便,下面咱們來看一下怎樣規避同源的限制。segmentfault


Cookie

是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。api

當兩個網頁的一級域名相同,只是二級域名不一樣的時候,咱們能夠經過設置document.domain來共享cookie
具體操做以下:跨域

// 這兩個網頁的一級域名是相同的 
http://h1.test.com
http://h2.test.com

//爲兩個頁面設置相同的 document.domain
document.domain = "test.com"

// 這樣兩個網頁就能共享`Cookie`

document.domain 不能隨意設置,只能把document.domain設置成自身或更高一級的父域。瀏覽器


跨域文檔通訊

若是兩個網頁不一樣源,就沒法拿到對方的DOM,也沒法進行通訊。典型的例子是iframe窗口和window.open方法打開的窗口,它們與父窗口沒法通訊。安全

若是兩個窗口一級域名相同,只是二級域名不一樣,那麼設置上一節介紹的document.domain屬性,就能夠規避同源政策,拿到DOM

關於通訊,咱們來看一下兩種解決方案:

片斷識別符

片斷標識符(fragment identifier)指的是,URL#號後面的部分,若是隻是改變片斷標識符,頁面不會從新刷新。

//父窗口能夠把信息,寫入子窗口的片斷標識符
var src = originURL + '#' + data;
document.getElementById('myIFrame').src = src;

//子窗口經過監聽hashchange事件獲得通知
window.onhashchange = checkMessage;

function checkMessage() {
  var message = window.location.hash;
  // ...
}


//一樣的,子窗口也能夠改變父窗口的片斷標識符
parent.location.href= target + "#" + hash;

window.postMessage

window.postMessage 是HTML5爲了解決這個問題,引入了一個全新的API,不管兩個窗口是否同源,都容許一個窗口向另外一個窗口發送數據。

語法:

// otherWindow 其餘窗口的一個引用,好比iframe的contentWindow屬性、執行window.open返回的窗口對象

//message 將要發送到其餘 window的數據

//targetOrigin 接收消息的窗口的源(origin)

otherWindow.postMessage(message, targetOrigin)

其餘window能夠監聽message

//監聽 message 事件

window.addEventListener("message", receiveMessage, false);

//事件對象有一些經常使用的屬性

//data 從其餘 window 中傳遞過來的對象

//origin 消息發送方窗口的 origin

//source 對發送消息的窗口對象的引用

function receiveMessage(event){
    var origin = event.origin;
    
    //對發送消息的源進行驗證
    
   if (origin !== "http://example.org:8080")
      return;

  // ...
}

實例:

窗口A : http://xiaoxiong.com
窗口B : http://miaomiao.com
顯然這兩個窗口不一樣源,不能通訊,如今咱們用postMessage進行通訊。

//Awindow、Bwindow分別表示對 A B 窗口對象的引用
//B窗口向A窗口發消息
//若是A窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,消息就不能發送成功
//注意是用 Awindow 調用 postMessage 方法
 Awindow.postMessage("hello!","http://xiaoxiong.com");
 
 //在A中設置監聽事件
 window.addEventListener("message", receiveMessage, false);
 
 function receiveMessage(event){
    console.log(event.origin);//http://miaomiao.com
    console.log(event.source);// Bwindow
    console.log(event.data);// hello!
    
}

AJAX

AJAX請求是咱們常常用到的異步請求方法,可是AJAX請求是不能跨域的。

下面咱們看一下常見的AJAX跨域方法

JSONP

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

function addScript(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script, body.firstChild);
}

window.onload = function () {
  //請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對於JSONP是必需的
  addScript('http://test.com/a?callback=handler');
}

//請求回來數據做爲回調函數的參數
//做爲參數的JSON數據被視爲JavaScript對象 不用進行轉換
function handler(data) {
  console.log(data);
};

JSONP的優勢:不受同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中均可以運行;而且在請求完畢後能夠經過調用callback的方式回傳結果。

JSONP的缺點:它只支持GET請求而不支持POST等其它類型的HTTP請求;它只支持跨域HTTP請求這種狀況,不能解決不一樣域的兩個頁面之間如何進行JavaScript調用的問題。


WebSocket

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

實例:

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,表示該請求的請求源(origin),即發自哪一個域名。
正是由於有了Origin這個字段,因此WebSocket纔沒有實行同源政策。由於服務器能夠根據這個字段,判斷是否許可本次通訊。若是該域名在白名單內,服務器就會作出以下回應。


CORS(Cross-origin resource sharing)

跨域資源共享 是官方的跨域解決方案。
整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。

基本思想
CORS定義了必須在訪問跨域資源時,瀏覽器與服務器應該如何溝通。CORS背後的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功仍是失敗。

基本流程

瀏覽器發現是跨源AJAX請求,瀏覽器就直接發出CORS請求。具體來講,就是在頭信息之中,增長一個Origin字段。

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

上面的頭信息中,Origin字段用來講明,本次請求來自哪一個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求。
若是Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequestonerror回調函數捕獲。注意,這種錯誤沒法經過狀態碼識別,由於HTTP迴應的狀態碼有多是200
若是Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段。

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

Access-Control-Allow-Origin
該字段是必須的。它的值要麼是請求時Origin字段的值,要麼是一個*,表示接受任意域名的請求。


CORSJSONP對比,更爲先進、方便和可靠。

一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。

二、 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。

三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS)。

四、 jsonp在調用失敗的時候不會返回各類HTTP狀態碼。

五、在請求完畢後能夠經過調用callback的方式回傳結果。將回調方法的權限給了調用方。這個就至關於將controller層和view層終於分開了。我提供的jsonp服務只提供純服務的數據,至於提供服務之後的頁面渲染和後續view操做都由調用者來本身定義就行了。若是有兩個頁面須要渲染同一份數據,大家只須要有不一樣的渲染邏輯就能夠了,邏輯均可以使用同 一個jsonp服務。

參考文獻

相關文章
相關標籤/搜索