瀏覽器出於安全方面的考慮,不一樣源的客戶端腳本在沒有明確受權的狀況下,不能讀寫對方的資源。javascript
同源指的是:html
同協議java
同域名ajax
同端口後端
做用:保證用戶信息的安全,防止惡意的網站竊取數據api
例1:A網站是一家銀行,用戶登陸之後,又去瀏覽其餘網站。若是其餘網站能夠讀取A網站的 Cookie,會發生什麼?很顯然,若是 Cookie 包含隱私(好比存款總額),這些信息就會泄漏。除此以外,Cookie 每每用來保存用戶的登陸狀態,若是用戶沒有退出登陸,其餘網站就能夠冒充用戶,隨心所欲。(由於瀏覽器同時還規定,提交表單不受同源政策的限制)跨域
例2:惡意網站的頁面經過iframe嵌入了銀行的登陸頁面(兩者不一樣源),若是沒有同源限制,惡意網頁上的javascript腳本就能夠在用戶登陸銀行的時候獲取用戶名和密碼,從而形成相關的風險。數組
對於當前頁面來講頁面中 JS 文件的域不重要,重要的是當前頁面所在的域與 腳本中涉及到的域(例如xht的open方法的url)是否同源瀏覽器
跨域:瀏覽器出於安全方面的考慮設置了同源策略來限制不一樣域之間的交互,可是也阻礙了不域之間的協助。爲了實現不一樣域之間的交互、協做,所以須要「跨域」。安全
降域獲取同一 Cookie:Cookie 是服務器寫入瀏覽器的一小段信息,只有同源的網頁才能共享。可是,兩個網頁一級域名相同,只是二級域名不一樣,瀏覽器容許經過設置document.domain 共享 Cookie。
example:A 網頁是 http://w1.example.com/a.html
,B 網頁是http://w2.example.com/b.html
,那麼只要設置相同的 document.domain
,兩個網頁就能夠共享 Cookie
。
JavaScript
// A網頁和B網頁設置相同的 document.domain document.domain = 'example.com'
// A網頁經過腳本設置 Cookie document.cookie = "test1 = hello";
// B網頁能夠獲取到該 Cookie var otherCookie = document.cookie;
降域使不一樣源的iframe窗口和父窗口相互通訊:若是兩個網頁不一樣源,就沒法拿到對方的
DOM。典型的例子是 iframe 窗口和與父窗口沒法通訊。若是兩個窗口一級域名相同,只是二級域名不一樣,那麼設置 document.domain 屬性,就能夠規避同源政策,拿到
DOM。
A 網頁:URL: http://a.yanxin.com:8080/a.html
HTML
<div class="ct"> <h1>使用降域實現跨域</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
<script> document.querySelector('.main input').addEventListener('input', function(){ console.log(this.value); /* window.frames 是窗口中全部命名的框架組成的數組。 這個數組的每一個元素都是一個Window對象,對應於窗口中的一個框架。 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 網頁:URL: http://b.yanxin.com:8080/b.html
HTML
<input id="input" type="text" placeholder="http://b.yanxin.com:8080/b.html">
script
<script> document.querySelector('#input').addEventListener('input', function(){ // 獲得父窗口中的input元素 window.parent.document.querySelector('input').value = this.value; }) document.domain = 'yanxin.com'; </script>
HTML5爲了解決跨域問題,引入了一個全新的API:跨文檔通訊 API(Cross-document messaging)。
這個API爲window對象新增了一個window.postMessage方法,容許跨窗口通訊,不論這兩個窗口是否同源。
目的:向另外一個地方傳遞數據,另外一個地方指的是:包含在當前頁面的<iframe>元素,或者由當前頁面彈出的窗口
window.postMessage() 方法被調用時,會在全部頁面腳本執行完畢以後向目標窗口派發一個 MessageEvent 消息。 * otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow:其餘窗口的一個引用(相對於當前的窗口的其餘窗口),好比iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames。
message將要發送到其餘 window的數據
targetOrigin:指定哪些窗口能接收到消息事件,其值能夠是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,若是目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那麼消息就不會被髮送;只有三者徹底匹配,消息纔會被髮送。
注意:若是你明確的知道消息應該發送到哪一個窗口,那麼請始終提供一個有確切值的targetOrigin,而不是*。不提供確切的目標將致使數據泄露到任何對數據感興趣的惡意站點。
示例
頁面 A: http://a.yanxin.cn:8080/a.html
。
在頁面 A 中 打開頁面 B: http://b.yanxin.cn:8080/b.html
當點擊頁面 A 上的 button 時,向頁面B傳輸消息 "hello world"
HTML
<button id="sendmessage">send message</button>
script
<script> var button = document.querySelector("#sendmessage"); // 打開頁面b,而且取得對它的引用 var targetWindow = window.open("http://b.yanxin.cn:8080/b.html"); button.addEventListener('click',function(){ // 注意: 這裏是向targetWindow即新打開的窗口(經過上面的window.open打開的窗口)發送消息 // 直接打開頁面b(手動打開的)是沒法收到消息的 // 若是直接調用postMessage則至關於當前窗口向本身發送消息 targetWindow.postMessage("hello world!!", "http://b.yanxin.cn:8080/b.html"); }); </script>
爲頁面 B 的 window 添加監聽器
當頁面 B 收到 Message 時,在控制檯中輸出收到的消息,並提示 "hello"
<script> window.addEventListener("message", receiveMessage); function receiveMessage(event){ console.log(event.data); alert("hello"); } </script>
CORS: Cross-Origin Resource Sharing, 跨源資源共享,是W3C的一個工做草案,定義了在必須訪問跨源資源時,瀏覽器與服務器應該如何溝通,它是一種 ajax跨域請求資源的方式,支持現代瀏覽器,IE支持10以上。
實現過程:當使用 XMLHttpRequest發送請求時,瀏覽器發現該請求不符合同源策略,會自動給該請求加一個請求頭:Origin,並將請求發送。服務器端收到請求後,若是肯定接受請求則在返回結果中加入一個響應頭:Access-Control-Allow-Origin; 瀏覽器收到響應後判斷該相應頭中是否包含 Origin 的值,若是有則瀏覽器會處理響應,咱們就能夠拿到響應數據,若是不包含瀏覽器直接駁回,這時咱們沒法拿到響應數據。整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與
實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。
詳細流程:
1.瀏覽器發現此次請求不符合同源策略,就自動在頭信息之中,添加一個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字段代表了本次請求來自哪一個源:協議、域名、端口。
2.該請求到達服務器後,服務器會根據這個值來判斷是否接受請求。若是Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段(以下所示)。若是Origin指定的源,不在許可範圍內,服務器會返回一個正常的HTTP迴應。
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.瀏覽器收到響應後判斷該相應頭中是否包含Origin的值,若是響應的頭信息沒有包含Access-Control-Allow-Origin字段,就知道出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調函數捕獲,這時咱們沒法拿到響應數據。若是有則瀏覽器會處理響應,咱們就能夠拿到響應數據。
基本思想是,網頁經過添加一個<script>元素,向服務器請求JSON數據,這種作法不受同源政策限制;服務器收到請求後,將數據放在一個指定名字的回調函數裏傳回來。傳回後回調函數當即執行(參數是後端產生的數據),從而實現相應的功能。
具體流程:
1.網頁動態地插入 <script> 元素,由它向跨域網址發出請求
JavaScript
function addScriptTag(src){ var script = document.createElement('script'); script.src = src; //跨域網址 document.body.appendChild(script); // 往頁面插入元素後,會向跨域網址發出請求(src指定了跨域網址,獲得響應後當即執行 } window.onload = function(){ addScriptTag("http://example.com/ip?callback = foo'); //當頁面加載完畢,即往頁面中插入script元素 } function foo(data){ console.log('your public ip address is: '+ data.ip)' }
2.上面代碼經過動態添加<script>元素,向服務器example.com發出請求。注意,該請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對於JSONP是必需的。
3.服務器收到該請求後,會將數據放在回到函數的參數位置返回
JavaScript
foo({ "ip": "8.8.8.8" });
4.因爲<script>元素請求的腳本,直接做爲代碼運行。這時,只要瀏覽器定義了foo函數,該函數就會當即調用。做爲參數的JSON數據被視爲JavaScript對象,而不是字符串,所以避免了使用JSON.parse的步驟。
注意:JSONP只能發 GET 請求(由於請求是放在<script>的scr中的)。
咱們知道,一個網頁能夠從任何網頁中加載圖像,不用擔憂跨域問題。這也是在線廣告跟蹤瀏覽量的主要方式。咱們能夠動態地建立圖像,使用它們的onload和onerror事件處理程序來肯定是否接受到了響應。
動態建立圖像常常用於圖像Ping,圖像Ping是與服務器進行簡單、單向的跨域通訊的一種方式。請求的數據是經過查詢字符串形式發送的,而響應能夠是任意內容,但一般是像素圖或者204響應。經過圖像Ping,瀏覽器得不到任何具體的數據,但經過偵聽load和error事件,它能知道響應是何時接受到的。
var img = new Image() img.onload = img.onerror = function(){ alert("Done") }; img.src = "http://www.example.com/test?name=Nicholas";
這裏建立了一個 Image 的實例,而後將 onload 和 onerror 事件處理程序指定爲同一個函數。這樣不管是什麼響應,只要請求完成,就能獲得通知。請求從設置 src 屬性那一刻開始,而這個例子在請求中發送了一個 name 參數。
圖像 Ping 最經常使用於跟蹤用戶點擊頁面或動態廣告曝光次數。
缺點:
只能發送GET請求
沒法訪問服務器的響應文本
結論:圖像Ping只能用於瀏覽器與服務器間的單向通訊。