跨域二三事

什麼是跨域?

簡單的說,沒有遵照瀏覽器「同源政策」的操做,就是跨域。html

什麼是「同源政策」?

  • 同源是說協議、域名、端口都相同。
  • 不一樣源下,緩存、dom獲取、ajax請求都會受到限制。
  • 同源政策的目的是保護用戶信息的安全,不然,cookie都共享了,用戶且不是能夠隨意冒充了?
    可是不少時候,咱們須要進行跨域操做,「同源政策」就成帶來了很大的不方便,如今看看如何合理的進行跨域知足你的須要吧。

訪問緩存

雖說 cookie 不能跨域, 可是子域之間、子父域之間是能夠共享的, 就是經過 document.domain .
cookie共享, eg:前端

// http://aa.test.com/a.html

// http://bb.test.com/bb.html

// 只要設置相同的主域, a.html & b.html 就能夠共享cookie
document.domain = 'test.com';

// a.html
document.cookie = "testData=helloWorld";

// b.html
var cookieYes = document.cookie;

還能夠在服務器設置cookie的一級域名, 這樣就不用在2、三級域名下設置了. eg:
Set-Cookie: key=value; domain=.test.com; path=/
PS: 這種方式, 只適用於 cookie 和 iframe 的訪問, localstorage 和 indexDB 都是不能夠的, 須要經過 postMessage.web

Iframe

在好久的之前, 對於不支持 XMLHttpRequest 的瀏覽器的最佳回溯方法之一就是使用 iframe 對象, 固然常規只是用來實現流模式的Comet.
不得不說, 一些正常沒法實現的功能/某些兼容的處理, 不少奇淫技巧都是藉助 iframe 完成的.ajax

看看藉助iframe在跨域中通訊.父窗口打開 http://test1.com , iframe子窗口打開 http://test2.com.json

hash url:

/* 父窗口給子窗口通訊 */
// test1.com
var src = url + '#' + data;
iframe.src = src;

// test2.com 監聽 hashchange
window.onhashchange = handleChange;
function handleChange() {
    var data = location.hash;
}

/* 子窗口給父窗口通訊 */
// test2.com
parent.location.href = url + '#' + data;

// ...

window.name:

// 子窗口 test2.com
window.name = data;

// 父窗口 test1.com
var data = iframe.contentWindow.name;

// window.name 能夠攜帶大量信息,可是這樣必須得監聽子窗口 window.name 的變化

window.postMessage

不論是 hash url, 仍是 window.name, 老是給人一種奇淫技巧的感受, 有木有? 接下來的這個就不同了, 它就是HTML5專爲了解決這樣問題而誕生的新朋友: 跨文檔通訊(Cross-document messaging)
核心的方法就是 window.postMesage , 無論倆窗口是否同源, 都容許跨窗口通訊.跨域

/* 窗口1 aa.com */
var popup = window.open('http://aa.com');

// 向 bb.com 發消息
popup.postMessage("hello, bb!",
                  "http://bb.com");

function receiveMessage(event) {
  // 能夠過濾掉不是 bb.com 傳來的消息
  if (event.origin !== "http://bb.com")
    return;

  // event.source is popup
  // event.data is "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);

/* 窗口2 bb.com */
function receiveMessage(event) {
  if (event.origin !== "http://aa.com")
    return;

  // event.source is window.opener
  // event.data is "hello, bb!"

  event.source.postMessage("hi there yourself!  the secret response is: rheeeeet!",
                          event.origin);
}
window.addEventListener("message", receiveMessage, false);

這樣子, localStorage 跨域也就能搞定啦.瀏覽器

AJAX

ajax 請求也是受到同源限制的, 若是我想訪問第三方網站的接口怎麼辦呢? 目前有三個辦法: jsonp / cors / websocket.緩存

jsonp:

基本思路是: 經過 script.src 請求 json 數據, 約定一個回調函數名將 json 包裹得到數據安全

function getScript(url) {
    var _script = document.createElement('script');
    _script.src = url;
    var body = document.getElementsByTagName("body")[0];
    body.appendChild(script);
}
function f(data) {
    alert(data.name);
}

getScript("http://test.com/jsonp?callback=f");

jsonp 簡單易用, 可是也只能發送發 get 請求.
ps: 實際上呢, 經過 src 方式的get請求, 都是不受同源限制, 能夠跨域的. 好比圖片 img.src = 'test1.com'.服務器

CORS:

跨域資源共享(Cross-origin resource sharing), 容許向跨域服務器發出 XMLHttpRequest 請求, 怎麼作到的呢?
基本思路: 使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通, 從而決定請求或響應是成功仍是失敗. 因此須要客戶和服務器兩端支持; 對於客戶端來講,感知不大, 和通常 ajax 請求彷佛沒有差異; 主要是對於服務端來講, 實現了 CORS 接口, 一切就ok.

CORS的處理流程:

  • 當你使用 XMLHttpRequest 發跨域請求時, 瀏覽器發現該請求不符合同源策略, 會給該請求加一個請求頭 Origin, 說明請求來源域.
  • 後臺進行一系列處理, 若是肯定接受請求則在返回結果中加入一個響應頭: Access-Control-Allow-Origin, 其值爲請求的 Origin 值.
  • 瀏覽器判斷該響應頭中是否包含了 Origin 的值, 若是有則瀏覽器會處理響應, 若是沒有瀏覽器直接駁回請求, 這時咱們沒法拿到響應數據.
  • 默認只支持 GET/POST 這兩種 http 請求類型, 若是要開啓 PUT/DELETE 之類的, 須要在服務端在添加一個 "Access-Control-Allow-Methods" 報頭標籤.
  • 請求和響應都不包含 cookie 值

IE10- 的兼容處理:

引入 XDR(XDomainRequest), 實現安全可靠的跨域通訊.

websoket:

websoket 是一種通訊協議, 基於 TCP/IP. 它不實施同源政策, 因此基本思路是, 只要服務器設置 origin 經過, 就能夠跨域

GET /xxx HTTP/1.1
Host: test1.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxxxxxxxxxxxxxxxxxxxx==
Sec-WebSocket-Protocol: xxx
Sec-WebSocket-Version: xx
Origin: http://test2.com

若是 Origin: http://test2.com 在白名單中, 就能夠進行通訊了.
服務器這是會把 http 轉換成 ws 協議

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: xxxxxxxxxxxxxxxxxxxxxxx==
Sec-WebSocket-Protocol: xxx

服務器代理:

基本思想: 首先將請求發送給後臺服務器, 經過服務器來發送請求, 而後將請求的結果傳遞給前端.

xhr.open('POST', 'http://localhost:8888/proxy?http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer', true);

參考《木的樹》

Comet

與Web服務器通訊(一)

WebSocket

與 Web 服務器通訊(三)

read more

《HTTP access control》

相關文章
相關標籤/搜索