前天面試被問到了跨域的問題,自我感覺回答的并不理想,下面我就分享一下整理后的總結分享給大家
一、為什么要跨域
安全限制
JavaScript或Cookie只能訪問同域下的內容——同源策略
同源策略
下表相對于: http://h5.jd.com/dir/ajax.js
注意
- 協議和端口造成的跨域問題,非前端解決范疇
- 所謂域,是通過“url首部”來識別,而非判斷域與ip的對應關系
(“URL的首部”指window.location.protocol +window.location.host)
二、跨域方案
1. jsonp
詳見博客 JSON 和 JSONP兩兄弟
2. cors
JSONP | CORS | |
目的 | 跨域 | 跨域 |
支持 |
get (受IE下url長度不能超過2083個字節的限制和出于安全考慮,一般不用來提交數據) |
所有類型的http請求 |
支持度 | 包括老式瀏覽器 | 不支持部分瀏覽器,移動端支持很好 |
缺點 |
1)安全問題(請求代碼中可能存在安全隱患) 2)確定jsonp請求是否失敗不太容易 3)只支持跨域HTTP請求這種情況,不能解決不同域的兩個頁面之間如何進行JavaScript調用的問題 |
支持率 |
原理 |
被包含在一個回調函數中的JSON 核心則是動態添加<script>標簽來調用服務器提供的js腳本 (允許用戶傳遞一個callback參數給服務端,然后服務端返回數據時會將這個callback參數作為函數名來包裹住JSON數據,這樣客戶端就可以隨意定制自己的函數來自動處理返回數據了) |
使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,還是應該失敗 (只需由服務器發送一個響應標頭即可) |
CORS需要瀏覽器和服務器同時支持
實現CORS通信的關鍵是服務器,只要服務器實現了CORS接口,就可以跨域通信
1)兩種請求方式
簡單請求、非簡單請求
a)簡單請求:
跨域時,瀏覽器自動在頭部信息中添加一個origin 字段(指定請求源-協議+域名+端口),如下圖所示
服務器判斷origin在域名許可范圍內,返回響應:
若不存在 Access-Control-Allow-Origin 字段,則出錯
以上頭部信息中,CORS相關字段有
- Access-Control-Allow-Origin
必須字段,其值為 origin / *(可接受任意域名請求) - Access-Control-Allow-Credentials
可選,是否允許發送Cookie - Access-Control-Expose-Headers
可選,是否需要Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma之外的字段
withCredentials 屬性
CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials
字段
Access-Control-Allow-Credentials: true
另一方面,開發者必須在AJAX請求中打開withCredentials
屬性。
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
注意
如果要發送Cookie,Access-Control-Allow-Origin
就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie才會上傳,其他域名的Cookie并不會上傳,且(跨源)原網頁代碼中的document.cookie
也無法讀取服務器域名下的Cookie。
b)非簡單請求(不同時滿足以上條件)
請求方法是PUT
或DELETE
,或者Content-Type
字段的類型是application/json
瀏覽器對于非簡單請求,就自動發出一個"預檢"請求,要求服務器確認可以這樣請求。下面是這個"預檢"請求的HTTP頭信息
除了Origin
字段,"預檢"請求的頭信息包括兩個特殊字段。
- Access-Control-Request-Method
該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT
。
- Access-Control-Request-Headers
該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header
。
2.CORS 支持度
3. iframe
(只有在主域相同時才能使用)
1)www.a.com/a.html中:
document.domain = 'a.com';
var ifr = document.createElement('iframe');
ifr.src = 'http://www.script.a.com/b.html';
ifr.display = none;
document.body.appendChild(ifr);
ifr.onload = function(){
var doc = ifr.contentDocument || ifr.contentWindow.document;
//在這里操作doc,也就是b.html
ifr.onload = null;
};
2) 在www.script.a.com/b.html中:
document.domain = 'a.com';
其他跨域方案
window.name:
在一個窗口(window)的生命周期內,窗口載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的所有頁面中的。
window.postMessage:
該方法是 HTML5 新引進的特性,可以使用它來向其它的window對象發送消息,無論這個window對象是屬于同源或不同源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。
動態創建script
JSONP也就是利用這個原理。
利用iframe和location.hash
淘汰類技術
利用flash
淘汰類技術
參考鏈接:
http://tech.jandou.com/cross-domain.html
http://www.cnblogs.com/JChen666/p/3399951.html
http://www.ruanyifeng.com/blog/2016/04/cors.html
文章列表