同domain(或ip),同端口,同協議視爲同一個域,一個域內的腳本僅僅具備本域內的權限,能夠理解爲本域腳本只能讀寫本域內的資源,而沒法訪問其它域的資源。這種安全限制稱爲同源策略。
出於安全考慮,HTML的同源策略不容許JavaScript進行跨域操做, 直接發送跨域 AJAX 會獲得以下錯誤:面試
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
隨着Web App的功能愈來愈強 各類跨域的需求催生了無數的跨域手法。甚至在HTML5標準中都給出了官方的跨域方法, 也是最近應付面試的須要,拿一篇文章來總結既有的各類跨域手段。json
這些跨域通訊的方法大體能夠分爲兩類:跨域
下面幾個域名下的頁面都是能夠經過document.domain跨域互操做的: http://a.com/foo, http://b.a.com/bar, http://c.a.com/bar。 但只能以頁面嵌套的方式來進行頁面互操做,好比常見的iframe方式就能夠完成頁面嵌套:瀏覽器
// URL http://a.com/foo var ifr = document.createElement('iframe'); ifr.src = 'http://b.a.com/bar'; ifr.onload = function(){ var ifrdoc = ifr.contentDocument || ifr.contentWindow.document; ifrdoc.getElementsById("foo").innerHTML); }; ifr.style.display = 'none'; document.body.appendChild(ifr);
上述代碼所在的URL是http://a.com/foo,它對http://b.a.com/bar的DOM訪問要求後者將 document.domain往上設置一級:安全
// URL http://b.a.com/bar document.domain = 'a.com'
document.domain只能從子域設置到主域,往下設置以及往其餘域名設置都是不容許的, 在Chrome中給出的錯誤是這樣的:服務器
Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'baidu.com' is not a suffix of 'b.a.com'
<img>, <script>
在document.body中append一個具備src屬性的HTML標籤, src屬性值指向的URL會以GET方法被訪問,該訪問是能夠跨域的。app
其實樣式表的
標籤也是能夠跨域的,只要是有src或href的HTML標籤都有跨域的能力。
不一樣的HTML標籤發送HTTP請求的時機不一樣,例如在更改src屬性時就會發送請求,而script, iframe, link[rel=stylesheet]只有在添加到DOM樹以後纔會發送HTTP請求:cors
var img = new Image(); img.src = 'http://some/picture'; // 發送HTTP請求 var ifr = $('<iframe>', {src: 'http://b.a.com/bar'}); $('body').append(ifr); // 發送HTTP請求
<script>
是能夠跨域的,並且在跨域腳本中能夠直接回調當前腳本的函數。<script>
能夠跨域的特性,跨域URL返回的腳本不只包含數據,還包含一個回調:// URL: http://b.a.com/foo var data = { foo: 'bar', bar: 'foo' }; callback(data);
該例子只用於示例,實際狀況應當考慮名稱隱藏等問題。
而後在咱們在主站http://a.com中,能夠這樣來跨域獲取http://b.a.com的數據:dom
// URL: http://a.com/foo var callback = function(data){ // 處理跨域請求獲得的數據 }; var script = $('<script>', {src: 'http://b.a.com/bar'}); $('body').append(script);
其實jQuery已經封裝了JSONP的使用,咱們能夠這樣來:函數
$.getJSON( "http://b.a.com/bar?callback=callback", function( data ){ // 處理跨域請求獲得的數據 });
$.getJSON與$.get的區別是前者會把responseText轉換爲JSON,並且當URL具備callback參數時, jQuery將會把它解釋爲一個JSONP請求,建立一個<script>
和全部依賴於建立HTML標籤的方式同樣,JSONP也不支持POST,而GET的數據是放在URL裏的。通常來說URL限長是在2000字符左右。
在如何理解HTTP響應的狀態碼?一文中有更多關於HTTP響應狀態碼的討論。
有些人注意到了IE6/7的一個漏洞:iframe之間的window.navigator對象是共享的。 咱們能夠把它做爲一個Messenger,經過它來傳遞信息。好比一個簡單的委託:
// a.com navigation.onData(){ // 數據到達的處理函數 } typeof navigation.getData === 'function' || navigation.getData()
// b.com navigation.getData = function(){ $.get('/path/under/b.com') .success(function(data){ typeof navigation.onData === 'function' || navigation.onData(data) }); }
與document.navigator相似,window.name也是當前窗口全部頁面所共享的。也能夠用它來傳遞信息。 一樣蛋疼的辦法還有傳遞Hash(有些人叫錨點),這是由於每次瀏覽器打開一個URL時,URL後面的#xxx部分會保留下來,那麼新的頁面能夠從這裏得到上一個頁面的數據。
前面提到的跨域手段都是某種意義上的Hack, HTML5標準中提出的跨域資源共享(Cross Origin Resource Share,CORS)纔是正道。 它支持其餘的HTTP方法如PUT, POST等,能夠從本質上解決跨域問題。
例如,從http://a.com要訪問http://b.com的數據,一般狀況下Chrome會因跨域請求而報錯:
XMLHttpRequest cannot load http://b.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://a.com' is therefore not allowed access.
錯誤緣由是被請求資源沒有設置Access-Control-Allow-Origin,因此咱們在b.com的服務器中設置這個響應頭字段便可:
Access-Control-Allow-Origin: * # 容許全部域名訪問,或者 Access-Control-Allow-Origin: http://a.com # 只容許XXX域名訪問
爲 xhr 設置 withCredentials 後 CORS 方法跨域還可 攜帶Cookie,但 PUT/POST 請求須要注意處理 preflight 請求。
能夠給任何一個window發送消息,不管是否同源。第二個參數能夠是*但若是你設置了一個URL但不相符,那麼該事件不會被分發。看一個普通的使用方式吧:
// URL: http://a.com/foo var win = window.open('http://b.com/bar'); win.postMessage('Hello, bar!', 'http://b.com');
// URL: http://b.com/bar window.addEventListener('message',function(event) { console.log(event.data); });
注意IE8及小於IE8的版本不支持addEventListener,須要使用attachEvent來監聽事件。 參見:事件處理中的this:attachEvent, addEventListener, onclick
在HTML5以前,JSONP已經成爲跨域的事實標準了,jQuery都給出了支持。 值得注意的是它只是Hack,並無產生額外的安全問題。 由於JSONP要成功獲取數據,須要跨域資源所在服務器的配合,好比資源所在服務器須要自願地回調一個合適的函數,因此服務器仍然有能力控制資源的跨域訪問。
跨域的正道仍是要使用HTML5提供的CORS頭字段以及window.postMessage, 能夠支持POST, PUT等HTTP方法,從機制上解決跨域問題。 值得注意的是Access-Control-Allow-Origin頭字段是資源所在服務器設置的, 訪問控制的責任仍然是在提供資源的服務器一方,這和JSONP是同樣的。