什么是同源策略
同源政策(same-origin policy)是指同域名(或ip),同端口鼻由,同協(xié)議視為同一個域蕉世,同域內(nèi)的腳本只能讀寫本域內(nèi)的資源,而無法訪問其它域的資源奸例,這種安全限制稱為同源策略查吊。當(dāng)一個瀏覽器的兩個tab頁中分別打開來百度和谷歌的頁面當(dāng)瀏覽器的百度tab頁執(zhí)行一個腳本的時候會檢查這個腳本是屬于哪個頁面的湖蜕,即檢查是否同源昭抒,只有和百度同源的腳本才會被執(zhí)行。如果非同源盗迟,那么在請求數(shù)據(jù)時罚缕,瀏覽器會在控制臺中報一個異常,提示拒絕訪問怎静。
同域指的是喂饥?
同協(xié)議:如都是http或者h(yuǎn)ttps
同域名:如都是http://baidu.com/a 和http://baidu.com/b
同端口:如都是8080端口
如果不是同源的员帮,會受到三種限制:
1.Cookie捞高、LocalStorage 和 IndexDB 無法讀取渣锦。
2. DOM 無法獲得袋毙。
3.AJAX 請求不能發(fā)送。
什么是跨域胀溺?跨域有幾種實現(xiàn)形式
跨域
跨域是指從一個域名的網(wǎng)頁去請求另一個域名的資源仓坞。比如從百度頁面去請求谷歌的資源腰吟。跨域的嚴(yán)格一點的定義是:只要 協(xié)議嫉称,域名织阅,端口有任何一個的不同始藕,就被當(dāng)作是跨域。
跨域有幾種實現(xiàn)形式
一.JSONP
JSONP是服務(wù)器與客戶端跨源通信的常用方法江耀。最大特點就是簡單適用祥国,兼容性好。主要利用html中script標(biāo)簽可以引入其他域的js文件啊犬,利用這個特性可以實現(xiàn)跨域訪問接口觉至,需要后端的支持睡腿。實現(xiàn)步驟:
1.定義數(shù)據(jù)處理函數(shù)fun:
2.網(wǎng)頁通過添加一個<script>元素席怪,src的地址執(zhí)行后端接口最后加個參數(shù)callback=fun,向服務(wù)器請求JSON數(shù)據(jù)碉纺,這種做法不受同源政策限制骨田;
3.服務(wù)器收到請求后疫赎,將數(shù)據(jù)放在fun回調(diào)函數(shù)里傳回來,輸出fun(data):
4.fun(data)會放到script標(biāo)簽作為js執(zhí)行捧搞,此時會調(diào)用fun(data),將data作為參數(shù)胎撇。
定義處理函數(shù)
function foo(data){
console.log(data.ip)
}
網(wǎng)頁動態(tài)插入<script>元素殖氏,由它向跨源網(wǎng)址發(fā)出請求。
function addScriptTag(src) {
var script = document.createElement('script');
script.src = src;
document.head.appendChild(script);
document.head.removeChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
服務(wù)器收到這個請求以后爵憎,會將數(shù)據(jù)放在回調(diào)函數(shù)的參數(shù)位置返回
foo({
"ip":"10.64.25.83"
})
由于<script>元素請求的腳本宝鼓,直接作為代碼運行愚铡。這時,只要瀏覽器定義了foo函數(shù)碍舍,該函數(shù)就會立即調(diào)用邑雅。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對象淮野,而不是字符串,因此避免了使用JSON.parse的步驟录煤。
缺點:
1.只能通過GET方式請求妈踊,參數(shù)長度有限制,安全性比較差
2.需要后端的支持
二.CORS
CORS全稱是"跨域資源共享"(Cross-origin resource sharing)歪泳。
它允許瀏覽器向跨源服務(wù)器呐伞,發(fā)出XMLHttpRequest請求慎式。支持現(xiàn)代瀏覽器瘪吏,IE10以上瀏覽器。CORS需要瀏覽器和服務(wù)器的支持蕾盯,因此级遭,實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器渺尘。只要服務(wù)器實現(xiàn)了CORS接口,就可以跨源通信掠兄。
當(dāng)CORS請求滿足下面的條件時
1.請求方法是以下三種方法之一:
HEAD
GET
POST
2.HTTP的頭信息不超出以下幾種字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data迅诬、text/plain
基本思想是
1.當(dāng)使用XMLHttpRequest()發(fā)送請求的時候侈贷,瀏覽器發(fā)現(xiàn)該請求不符合同源策略等脂,會給該請求的頭部信息添加一個origin字段上遥,Origin字段用來說明,本次請求來自哪個源辣恋,服務(wù)器根據(jù)這個值模软,決定是否同意這次請求。
如果Origin指定的源携狭,不被服務(wù)器允許逛腿,服務(wù)器也會返回正常的HTTP響應(yīng)鲫剿,瀏覽器發(fā)現(xiàn)響應(yīng)頭沒有包含origin字段,就拋出錯誤,被onerror回調(diào)函數(shù)捕獲政冻,這種錯誤狀態(tài)碼無法識別。
2.如果指定的源明场,被服務(wù)器允許苦锨,服務(wù)器返回響應(yīng)信息的響應(yīng)頭會包含origin的信息,如下:
三.document.domain(也就是降域)
document.domain用于主域相同子域不同的場景
降域的設(shè)置也是有限制的拉庶,只能把document.domain氏仗,設(shè)置成自身或者更高一級的域夺鲜,且主域必須相同币励,如:a.b.test.com中的某個文檔的域可以設(shè)置成a.b.test.com、b.test.com流炕、test.com搁进。但是不可以設(shè)置成.com或者c.a.b.test.com或者baidu.com饼问,因為baidu.com主域和當(dāng)前域不同了。
使用方法
a頁面中加入document.domain = ‘test.com’;
b頁面中加入document.domain = ‘test.com’;
a.index.html
<div class="ct">
<h1>使用降域?qū)崿F(xiàn)跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.test.com:8080/a.html">
</div>
<iframe src="http://b.test.com:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "test.com"
b.index.html
<div>
<input id="input" type="text" placeholder="http://b.test.com:8080/b.html">
</div>
<script>
document.querySelector('#input').addEventListener('input', function(){
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'test.com';
</script>
</html>
四峻堰,postMessage
postMessage是html5新增的方法捐名,可以實現(xiàn)跨文本檔镶蹋、多窗口赏半、跨域消息傳遞。該方法可以通過綁定window的message事件來監(jiān)聽發(fā)送跨文檔消息傳輸內(nèi)容拂酣。postMessage(data,origin)方法接受兩個參數(shù):
1.data:要傳遞的數(shù)據(jù)婶熬。
2.origin:字符串參數(shù),指明目標(biāo)窗口的源虽另,協(xié)議+主機+端口號[+URL]性含,URL會被忽略,所以可以不寫叠萍,這個參數(shù)是為了安全考慮postMessage()方法只會將message傳遞給指定窗口苛谷,如果設(shè)置為"*",這樣可以傳遞給任意窗口腹殿。
http://a.test.com/a.html
<div class="main">
<input type="text" placeholder="http://a.test.com/a.html">
</div>
<iframe src="http://b.test.com/b.html" frameborder="0" ></iframe>
<script>
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
$('.main input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
在http://a.test.com/a.html通過postMessage()方法向跨域的iframe頁面http://b.test.com/b.html傳遞消息,b.html監(jiān)聽window的message事件就可以
<input id="input" type="text" placeholder="http://b.test.com/b.html">
<script>
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');
})
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
JSONP 的原理是什么
JSONP不是最新的數(shù)據(jù)格式,是json跨域獲取的解決方案决采,通過jsonp獲取的數(shù)據(jù)作為js的參數(shù)運行。瀏覽器有同源策略树瞭,會把跨域請求都禁止了晒喷,但是html的<script>標(biāo)簽,不受同源策略的影響衣盾,可以從其他源獲取數(shù)據(jù)爷抓,所以我們可以通過script標(biāo)簽废赞,引入其他源的數(shù)據(jù)唉地,通過js代碼進(jìn)行解析。先定義處理函數(shù)functionname极颓,然后通過添加一個<script>元素群嗤,src的地址是后端接口,在地址后加個請求參數(shù)callback=函數(shù)名骇径,這個就是處理函數(shù)的函數(shù)名破衔,向服務(wù)器請求JSON數(shù)據(jù)晰筛,服務(wù)器收到請求后拴袭,把數(shù)據(jù)放在回調(diào)函數(shù)中返回,返回的functionname(data)會放在script標(biāo)簽作為js執(zhí)行,所以會調(diào)用functionname(data),將data作為函數(shù)的參數(shù)怜瞒。
CORS是什么
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫盼砍。它是W3C標(biāo)準(zhǔn)浇坐,是跨源AJAX請求的根本解決方法黔宛。相比JSONP只能發(fā)GET請求臀晃,CORS允許任何類型的請求。CORS需要瀏覽器和服務(wù)器同時支持案淋,現(xiàn)代瀏覽器都支持此功能踢京,IE瀏覽器要IE10以上誉碴。CORS的整個通信過程不需要用戶參與黔帕,瀏覽器會自己完成蹈丸,CORS與AJAX的同源通信一樣逻杖,在發(fā)送AJAX請求的時候奋岁,瀏覽器會自動在頭部添加一個origin字段厦取,只要服務(wù)器允許cors的約定虾攻,就可以實現(xiàn)跨域通信更鲁。
CORS兩種請求
cors有簡單請求和非簡單請求霎箍,只要滿足
1)請求方法是HEAD/GET/POST三種方法之一;
2)HTTP的頭信息不超出一下幾種字段:Accept/Accept-Encoding/Accept-Language/Cache-Control/Connection/Cookie/Host/If-Modified-Since/Referer/User-Agent/Content-Type/Content-Language澡为。其中Content-Type僅限于三個值:application/x-www-form-urlencoded漂坏、multipart/form-data、text/pain媒至。就是簡單請求顶别,如果不滿足就是非簡單請求,
簡單請求
對于簡單請求拒啰,瀏覽器直接發(fā)cors請求驯绎,在頭部添加origin字段,origin字段用來說明本次請求來自哪個源谋旦,服務(wù)器會根據(jù)這個origin剩失,決定是否同意通信册着,如果服務(wù)器不同意,也會返回正常的http響應(yīng)绽媒,瀏覽器沒發(fā)現(xiàn)有origin這個字段,就是報錯获三,表示不能跨域通信。
** 非簡單請求**
非簡單請求是對服務(wù)器有特殊要求的請求,比如請求方法是PUT/DELETE裸弦,或者Content-Type字段的類型是application/json。
非簡單請求的CORS請求窖贤,會在正式itongxin之前豌熄,增加愛一次HTTP查詢請求粤攒,叫預(yù)檢請求(preflight)纷妆。
??瀏覽器現(xiàn)詢問服務(wù)器逊拍,當(dāng)前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單中,以及可以使用哪些HTTP動詞和頭信息字段谴咸。只有得到肯定答復(fù),瀏覽器才會發(fā)出正式的XMLHttpRequest請求,否則就報錯叶洞。
除了Origin字段,預(yù)檢請求的頭信息還包括兩個特殊字段:
??(1). Access-Control-Request-Method
??該字段是必須的叶雹,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,例子中是POST谦炒。
??(2). Access-Control-Request-Headers
??該字段是都好分割的字符串魂莫,指定瀏覽器CORS請求會額外發(fā)送的頭信息字段,上面的例子默認(rèn)application/json對應(yīng)的額外字段是”Content-Type”山卦。
服務(wù)器收到預(yù)檢請求后藻懒,檢查Origin酷含、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,確認(rèn)允許跨源請求弥虐,并做出回應(yīng)。
如果服務(wù)器否定了預(yù)檢請求颖对,會返回一個正常的HTTP Response,但是沒有任何CORS相關(guān)的頭信息字段。這時候瀏覽器認(rèn)為i額服務(wù)器不同意預(yù)檢請求坑鱼,因此出發(fā)一個錯誤,被XMLHttpRequest對象的onerror毀掉函數(shù)捕獲。控制臺會打印出如下報錯信息。
一旦服務(wù)器通過了預(yù)檢請求,以后每次瀏覽器正常的CORS請求就都與簡單請求一樣粥脚,包括Origin字段信息搀菩。服務(wù)器的回應(yīng)也會有Access-Control-Allow-Origin字段
根據(jù)視頻里的講解演示三種以上跨域的解決方式 州既,寫成博客
最后一個cors的介紹參考了阮一峰老師的日志实束。