講跨域以前,咱們先來說同源策略(SOP),同源策略是網景公司提出的一個著名安全策略。所謂同源就是域名、協議、端口相同。例如http://www.12306.cn中,http就是超文本傳輸協議,12306就是域名,cn就是端口。若是兩個資源須要通訊,那麼他必須知足SOP。而在前端中咱們使用ajax進行數據請求。
若是資源不一樣域,那麼咱們在使用ajax請求數據的時候,就會報錯,表示拒絕訪問。那如何進行跨域處理呢?事實上有三種方法一、JSONP,二、子域代理,三、CORS。因爲第二種方法現在已經採用的很是少,因此咱們在這兒不作講解javascript
帶填充的JSON,是一種能夠在JS中繞過同源策略,併發起跨域HTTP請求的使用模式,能夠啓動JS的跨域HTTP請求
同源策略有一個顯著的例外,HTML腳本元素是能夠規避SOP檢查的。那就意味着咱們能夠採用動態注入腳本的方式向其餘源發出HTTP請求。JSONP正是利用了這個例外狀況進行跨域數據加載的。html
咱們先來看一個例子:使用ajax請求一個普通的JSON文件。假設你使用ajax請求'http://jsonpjs.com/info.json',它會返回一個JSON文檔,其中包含一些信息前端
{ 'title': 'jsonp explaintation', 'author': 'Cornelius' }
瀏覽器接受到這個json文件後,就會把他當成字符串進行處理,可是這個字符串咱們須要把它轉換爲對象,纔可以被javascript所使用,這裏咱們就可使用json.parse函數來完成。固然因爲同源策略的限制,ajax只可以在同一個域中才可以使用。可是正如咱們前面提到的,script是html腳本元素它能夠規避SOP的檢查因此咱們爲了請求到json文件,咱們可使用這種方式java
<script src='http://jsonpjs.com/info.json'></script>
經過script元素請求資源,當文件加載完成時,瀏覽器會把json響應看成Javascript解析。可是這樣的狀況下咱們仍是沒法得到json數據。
因爲該對象沒有被存儲,也沒有賦值給一個變量,或者做爲參數傳遞給一個函數,瀏覽器就會忽略它。
那麼該如何獲取JSON輸出呢?
這兒有兩種方法,第一種直接由服務器把json數據進行存儲。例若有一個外部URL,http://jsonjs.com//info.js(注意文件擴展名是.js而不是.json),內容以下git
var jsonResponse = { 'title': 'jsonp explaintation', 'author': 'Cornelius' }
當文件加載完成後,咱們就能夠經過全局變量jsonResponse訪問這個JSON對象了。當該變量包含所請求數據時,咱們可使用script.onload來通知代碼。那麼另一種經過一個回調函數代替全局變量的方式來傳遞JSON對象github
jsonHandler({ 'title': 'jsonp explaintation', 'author': 'Cornelius' })
使用這種方式的好處在於,咱們不須要依靠script元素的onload事件來判斷json是否可用,當info.js被解析時,回調便會自動執行。這須要加載<script>元素以前,在全局環境下定義好這個回調函數
經過<script>加載json的方法最大的缺點就是:應用程序加載這些文件時,須要預先知道全局變量或者回調函數的名稱。下面是實現jsonp的簡單實例ajax
window.jsonpCallback = function (json) { // 處理這個json數據 } var script = document.createElement('script') script.src = 'http://jsonjs.com//info.js?callback=jsonpCallback' document.body.appendChild(script)
這種跨域技術很是的簡單和強大,可是他也有一些侷限性和安全性
JSONP僅適用於http的get請求。只能使用GET請求就意味着不少限制,提交到服務器的數據量將受限於瀏覽器的最大URL長度。JSONP缺少錯誤處理機制,若是腳本注入成功後,就會調用回調函數,可是注入失敗後,沒有任何提示。這就意味着,當JSONP遇到40四、505或者其餘服務器錯誤時,你是沒法檢測出錯緣由的。咱們可以作的也只有超時,沒有收到響應,便認爲請求失敗,執行對應的錯誤回調。
在安全方面,藉助JSONP有可能進行跨站請求僞造(CSRF)攻擊,當一個惡意網站使用訪問者的瀏覽器向服務器發送請求並進行數據變動時,被稱爲CSRF攻擊。因爲請求會攜帶cookie信息,服務器會認爲是用戶本身想要提交表單或者發送請求,而獲得用戶的一些隱私數據json
CORS是經過一系列特殊的HTTP頭來解決這一問題,這些http頭信息能夠容許雙方判斷請求成功或者失敗
在發送跨域HTTP請求時,支持CORS的瀏覽器會引入額外的Origin頭信息來指定請求的源。這個頭信息須要包含三個部分——協議、域名和端口
Origin: http://www.example.com
服務端的工做是檢查頭信息是否接受該請求。若是請求被接受,那麼須要返回一個包含Access-Control-Allow-Origin,其值與客戶端Origin值相同的響應頭
Access-Control-Allow-Origin: http://www.example.com
若是資源是公共的容許任何源發送請求,服務器能夠返回一個通配符
Access-Control-Allow-Origin: *
若是匹配成功,那麼瀏覽器將會繼續處理這個請求,不然禁止該請求。
一個使用CORS發起跨域請求的函數跨域
function makeCORSRequest(url, method) { if (typeof XMLHttpRequest === 'undefined') { return null } var xhr = new XMLHttpRequest() if ('withCredentials' in xhr) { xhr.open(method, url, true) // 標準瀏覽器支持cors } else if (typeof XDomainRequest !== 'undefined') { xhr = new XDomainRequest() // 支持cors的IE瀏覽器 xhr.open(method, url) } else { xhr = null // 不支持CORS的瀏覽器 } }
默認狀況下,使用CORS發送請求時,瀏覽器不會發送任何識別信息,如cookie或者http認證頭,爲了發送認證信息,必須將XMLHttpRequest對象的withCredentials屬性設置爲true
var xhr = new XmlHttpRequest()
xhr.withCredentials = true
若是瀏覽器支持識別信息,那麼須要在響應頭中返回Access-Control-Allow-Crendentials。
Access-Control-Allow-Crendentials:true
使用Cors時,若是請求方法不是GET,POST或者HEAD,或者使用了自定義的HTTP頭,瀏覽器將會發起所謂的預檢請求,預檢要求就是判斷請求是否合法
客戶端會發送如下信息:瀏覽器
Origin---請求的源 Access-Control-Request-Method---Http請求方法 Access-Control-Request-Headers----以逗號分隔的請求自定義頭 而後服務器返回的請求頭 Access-Control-Allow-Origin----容許的請求源 Access-Control-Allow-Methods-----以逗號分隔的容許方法列表 Access-Control-Allow-Headers------以逗號分隔的容許頭信息 Access-Control-Max-Age----預檢請求的緩存時間 Access-Control-Allow-Crendentials------指名所請求的資源是否支持認證信息
當客戶端收到服務器返回的信息後,就會使用前面申明的http方法進行請求
不過cors預檢支持頗有限:只有Firefox, Safari和Chrome。
下面是瀏覽器對CORS的兼容性表格
個人github若是對你有幫助請點一個star
第一次寫技術文章,不足的地方還請各位多多指教
參考文獻:跨域資源共享 CORS 詳解 ---阮一峯
[third-party javascript ---- Ben Vinegar Anton Kovalyov][2]