同源是指協議相同、域名相同、端口相同。同源政策的目的,是爲了保證用戶信息的安全,防止惡意的網站竊取數據。javascript
同源策略主要限制下面三種狀況html
Cookie
沒法讀取java
DOM
沒法得到web
AJAX
請求不能發送json
同源策略的本意是爲了保證用戶的信息安全。但有時也會帶來不便,下面咱們來看一下怎樣規避同源的限制。segmentfault
是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。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 是
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
跨域方法
基本思想是,網頁經過添加一個
<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
是一種通訊協議,使用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通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,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
字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest
的onerror
回調函數捕獲。注意,這種錯誤沒法經過狀態碼識別,由於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
字段的值,要麼是一個*
,表示接受任意域名的請求。
CORS
和JSONP
對比,更爲先進、方便和可靠。
一、 JSONP只能實現GET請求,而CORS支持全部類型的HTTP請求。 二、 使用CORS,開發者可使用普通的XMLHttpRequest發起請求和得到數據,比起JSONP有更好的錯誤處理。 三、 JSONP主要被老的瀏覽器支持,它們每每不支持CORS,而絕大多數現代瀏覽器都已經支持了CORS)。 四、 jsonp在調用失敗的時候不會返回各類HTTP狀態碼。 五、在請求完畢後能夠經過調用callback的方式回傳結果。將回調方法的權限給了調用方。這個就至關於將controller層和view層終於分開了。我提供的jsonp服務只提供純服務的數據,至於提供服務之後的頁面渲染和後續view操做都由調用者來本身定義就行了。若是有兩個頁面須要渲染同一份數據,大家只須要有不一樣的渲染邏輯就能夠了,邏輯均可以使用同 一個jsonp服務。
參考文獻