說到跨域,咱們就不得不先提一下同源。javascript
同源是瀏覽器的一種安全策略,所謂同源是指,域名,協議,端口徹底相同,而不一樣源就是跨域。也就是說咱們若是域名,協議,端口只要有一個不是不一樣的那麼就是跨域。php
舉個例子說:http://www.example.com/html
http://api.example.com/detail.html 不一樣源 域名不一樣 https//www.example.com/detail.html 不一樣源 協議不一樣 http://www.example.com:8080/detail.html 不一樣源 端口不一樣 http://api.example.com:8080/detail.html 不一樣源 域名、端口不一樣 https://api.example.com/detail.html 不一樣源 協議、域名不一樣 https://www.example.com:8080/detail.html 不一樣源 端口、協議不一樣 http://www.example.com/detail/index.html 同源 只是目錄不一樣
經過上面的例子,相信你們都對同源和跨域有了必定的瞭解。瀏覽器出於安全考慮,同源策略不容許跨域調用其餘頁面的對象。可是安全限制的同時也給咱們使用iframe或者獲取其餘服務器上的接口數據帶來了很多的麻煩。前端
由於咱們在所不免的會須要調用其餘服務器的接口,因此提供了一下幾個跨域的方案。html5
前面咱們說到瀏覽器的同源策略限制了不一樣源的iframe之間進行的DOM操做,可是須要說明的一點是,在不一樣的iframe之間(父子或同級)是可以獲取到彼此window對象的。java
document.domain解決了在頂級域名相同的狀況下可是域名不徹底相同的跨域問題.jquery
好比:咱們有一個頁面它的地址是: http://www.study.com/domain.html 在這個頁面中有一個有一個iframe,它的src是http://api.study.com/domain.html,代碼以下web
<body> <p>我是父窗口study.com的內容</p> <iframe id="iframe" src="http://api.study.com/domain.html" frameborder="0"> </iframe> <script> // 這種狀況適合 頂級域名相同的狀況 document.domain = 'study.com'; var iframe = document.getElementById('iframe'); iframe.onload = function () { var contentWin = iframe.contentWindow; contentWin.document.getElementById('txt').style.color = 'red'; } </script> </body>
很顯然這個頁面和它裏面的iframe來自不一樣的域,咱們默認是沒法相互操做對方的DOM元素的,可是若是咱們都將兩個頁面的document.domain 設爲相同的域名即:document.domain = 'study.com'就能夠操做對方DOM元素了。iframe頁面的代碼以下ajax
<body> <p id="txt">我是api.study.com域下的一個頁面</p> <script> document.domain = 'study.com'; // 訪問父級 top.document.getElementsByTagName('p')[0].style.color = 'red'; </script> </body>
須要說明的是:document.domain的設置是有限制的,咱們只能把document.domain設置成自身或更高一級的父域,且頂級域名必須相同。 例如:a.b.study.com 中的某個頁面的document.domain 能夠設成a.b.study.com、b.study.com 、study.com中的任意一個,可是不能夠設成 c.a.b.study.com,由於這是當前域的子域,也不能夠設成baidu.com,由於頂級域名已經不相同了。json
在實現了兩個頁面的DOM元素訪問也就至關於實現了兩個頁面之間的數據傳遞,這個就須要咱們一塊兒發散思惟了。
咱們前面說到在不一樣的iframe之間(父子或同級)是可以獲取到彼此window對象的。咱們能夠經過window.name實現的跨域數據傳輸。window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的全部頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的的載入而進行重置。
效果實現: 咱們須要準備三個頁面:
www.study.com/name.html //應用頁面。
<body> <!-- setp 1 --> <iframe id="iframe" src="http://api.study.com/name.html" frameborder="0"> </iframe> <button id="btn">獲取數據</button> <script> var iframe = document.getElementById('iframe'); // 將數據填加到name上了 iframe.contentWindow.name = 100; iframe.onload = function () { this.src = 'proxy.html'; this.onload = null; } var btn = document.getElementById('btn'); btn.onclick = function () { var text = iframe.contentWindow.name; document.write('<p>我是從api.study.com中獲得值' + text + '</p>'); } </script> </body>
www.study.com/proxy.html //代理文件,通常是一個沒有任何內容的html文件,須要和應用頁面在同一域下。在應用頁面動態建立iframe
api.study.com/name.html //應用頁面須要獲取數據的頁面,可稱爲數據頁面。
<body> <p id="txt">我是api.study.com域下的一個頁面</p> <script> window.name = 400; </script> </body>
你們都知道location是javascript裏邊BOM中管理地址欄的內置對象,好比location.href就管理頁面的url,用location.href=url就能夠直接將頁面重定向url。而location.hash則能夠用來獲取或設置頁面的標籤值。好比http://domain/#admin的location.hash="#admin"。
它的原理和window.name有些相似都須要經過一個同源文件做爲代理文件,和window.name同樣咱們也須要準備三個頁面。
www.study.com/hash.html //應用頁面。
<body> <script type="text/javascript"> function getData(url, fn) { var iframe = document.createElement('iframe'); iframe.style.display = 'none'; iframe.src = url; iframe.onload = function() { fn(iframe.contentWindow.location.hash.substring(1)); window.location.hash = ''; document.body.removeChild(iframe); }; document.body.appendChild(iframe); } // get data from server var url = 'http://api.study.com/hash.php'; getData(url, function(data) { var jsondata = JSON.parse(data); console.log(jsondata.name + ' ' + jsondata.age); }); </script> </body>
www.study.com/proxy.html //代理文件,須要和應用頁面在同一域下。
api.study.com/hash.php //應用頁面須要獲取數據的頁面,可稱爲數據頁面。
<?php // 若是有必要則進行數據處理 $_GET['..'] // code // 返回的數據 $data = '{\"name\":\"zs\",\"age\":10}'; echo "<script> window.location = 'http://localhost/proxy.html' + '#' + \"$data\"; </script>"; ?>
window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
postMessage(data,origin)方法接受兩個參數
1.data:要傳遞的數據,html5規範中提到該參數能夠是JavaScript的任意基本類型或可複製的對象,然而並非全部瀏覽器都作到了這點兒,部分瀏覽器只能處理字符串參數,因此咱們在傳遞參數的時候須要使用JSON.stringify()方法對對象參數序列化,在低版本IE中引用json2.js能夠實現相似效果。
2.origin:字符串參數,指明目標窗口的源,協議+主機+端口號[+URL],URL會被忽略,因此能夠不寫,這個參數是爲了安全考慮,postMessage()方法只會將message傳遞給指定窗口,固然若是願意也能夠建參數設置爲"*",這樣能夠傳遞給任意窗口,若是要指定和當前窗口同源的話設置爲"/"。
此次咱們只須要兩個頁面便可
www.study.com/postMessage.html
<body> <iframe id="iframe" src="http://api.study.com/postmessage.html" frameborder="0"> </iframe> <script> var iframe = document.getElementById('iframe'); iframe.onload = function () { // 向哪一個域下傳值 iframe.contentWindow.postMessage('red', 'http://api.study.com'); } </script> </body>
api.study.com/possMessage.html
<body> <p id="txt">我是api.study.com域下的一個頁面</p> <script> window.addEventListener('message', function(ev) { document.getElementById('txt').style.color = ev.data; }); </script> </body>
全名爲:JSON with Padding
咱們都知道link 、 script 、img 標籤都有跨域的能力,而jsonp的本質就是利用了script標籤的可跨域的特性,由服務端返回一個預先定義好的Javascript函數的調用,而且將服務器數據以該函數參數的形式傳遞過來,此方法須要先後端配合完成。
前端頁面:www.study.com/jsonp.html
<script> function fuck(data){ var data = JSON.parse(data); console.log(data); } </script> <script src="http://api.study.com/jsonp.php?callback=fuck"></script>
後臺頁面api.study.com/jsonp.php
<?php header('Content-Type:text/html;charset=utf-8'); $callback = $_GET['callback']; $json = '{"name":"xjj","age":"10"}'; echo $callback."('$json')"; ?>
在咱們經常使用的jquery中的調用時集合在了ajax方法中,
將設置dataType值爲jsonp即開啓跨域訪問
jsonp 能夠指定服務端接收的參數的「key」值,默認爲callback
jsonpCallback 能夠指定相應的回調函數,默認自動生成
前端調用 www.study.com/jquery_jsonp.html
$.ajax({ type:'get', url:'http://api.study.com/jquery_jsonp.php', dataType:'jsonp', jsonp:'callback', success:function(data){ console.log(data); } });
後臺頁面 api.study.com/jquery_jsonp.php
<?php header('Content-Type:text/html;charset=utf-8'); /*處理業務邏輯 返回數據給第三方過的的接口*/ $callback = $_GET['callback']; $json = '{"name":"xjj","age":"18"}'; echo $callback.'('.$json.')'; ?>
當前幾乎全部的瀏覽器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)均可經過名爲跨域資源共享(Cross-Origin Resource Sharing)的協議支持ajax跨域調用。
假設您的頁面在 www.study.com 上了,而您想要從 www.learn.com 提取數據。通常狀況下,若是您嘗試進行這種類型的 AJAX 調用,請求將會失敗,而瀏覽器將會出現「源不匹配」的錯誤。利用 CORS,www.learn.com 服務端只需添加一個HTTP Response頭,就能夠容許來自 www.study.com 的請求:
Access-Control-Allow-Origin: http://example.com Access-Control-Allow-Credentials: true(可選)
可將 Access-Control-Allow-Origin 添加到某網站下或整個域中的單個資源。要容許任何域向你提交請求,請設置以下:
Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true(可選)
啓用開發人員工具後,您就會在響應中看到 Access-Control-Allow-Origin 。
若是服務器端已啓用了 CORS,那麼提交跨域請求就和普通的 XMLHttpRequest 請求沒什麼區別。例如,如今 example.com 能夠向 www.example2.com 提交請求了:
$.ajax({ type:'get', url:'http://api.study.com/CORS.php', dataType:'jsonp', success:function(data){ console.log(data); } });
CORS在移動終端支持的不錯,能夠考慮在移動端全面嘗試;PC上有不兼容和沒有完美支持。
CORS提供了一種跨域請求方案,但沒有爲安全訪問提供足夠的保障機制,若是你須要信息的絕對安全,不要依賴CORS當中的權限制度,應當使用更多其它的措施來保障。