什么是同源策略
同源政策(same-origin policy)是指同域名(或ip),同端口,同協議視為同一個域,同域內的腳本只能讀寫本域內的資源,而無法訪問其它域的資源,這種安全限制稱為同源策略。當一個瀏覽器的兩個tab頁中分別打開來百度和谷歌的頁面當瀏覽器的百度tab頁執行一個腳本的時候會檢查這個腳本是屬于哪個頁面的,即檢查是否同源,只有和百度同源的腳本才會被執行。如果非同源,那么在請求數據時,瀏覽器會在控制臺中報一個異常,提示拒絕訪問。
同域指的是?
同協議:如都是http或者https
同域名:如都是http://baidu.com/a 和http://baidu.com/b
同端口:如都是8080端口
如果不是同源的,會受到三種限制:
1.Cookie、LocalStorage 和 IndexDB 無法讀取。
2. DOM 無法獲得。
3.AJAX 請求不能發送。
什么是跨域?跨域有幾種實現形式
跨域
跨域是指從一個域名的網頁去請求另一個域名的資源。比如從百度頁面去請求谷歌的資源。跨域的嚴格一點的定義是:只要 協議,域名,端口有任何一個的不同,就被當作是跨域。
跨域有幾種實現形式
一.JSONP
JSONP是服務器與客戶端跨源通信的常用方法。最大特點就是簡單適用,兼容性好。主要利用html中script標簽可以引入其他域的js文件,利用這個特性可以實現跨域訪問接口,需要后端的支持。實現步驟:
1.定義數據處理函數fun:
2.網頁通過添加一個<script>元素,src的地址執行后端接口最后加個參數callback=fun,向服務器請求JSON數據,這種做法不受同源政策限制;
3.服務器收到請求后,將數據放在fun回調函數里傳回來,輸出fun(data):
4.fun(data)會放到script標簽作為js執行,此時會調用fun(data),將data作為參數。
定義處理函數
function foo(data){
console.log(data.ip)
}
網頁動態插入<script>元素,由它向跨源網址發出請求。
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');
}
服務器收到這個請求以后,會將數據放在回調函數的參數位置返回
foo({
"ip":"10.64.25.83"
})
由于<script>元素請求的腳本,直接作為代碼運行。這時,只要瀏覽器定義了foo函數,該函數就會立即調用。作為參數的JSON數據被視為JavaScript對象,而不是字符串,因此避免了使用JSON.parse的步驟。
缺點:
1.只能通過GET方式請求,參數長度有限制,安全性比較差
2.需要后端的支持
二.CORS
CORS全稱是"跨域資源共享"(Cross-origin resource sharing)。
它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求。支持現代瀏覽器,IE10以上瀏覽器。CORS需要瀏覽器和服務器的支持,因此,實現CORS通信的關鍵是服務器。只要服務器實現了CORS接口,就可以跨源通信。
當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.當使用XMLHttpRequest()發送請求的時候,瀏覽器發現該請求不符合同源策略,會給該請求的頭部信息添加一個origin字段,Origin字段用來說明,本次請求來自哪個源,服務器根據這個值,決定是否同意這次請求。
如果Origin指定的源,不被服務器允許,服務器也會返回正常的HTTP響應,瀏覽器發現響應頭沒有包含origin字段,就拋出錯誤,被onerror回調函數捕獲,這種錯誤狀態碼無法識別。
2.如果指定的源,被服務器允許,服務器返回響應信息的響應頭會包含origin的信息,如下:
三.document.domain(也就是降域)
document.domain用于主域相同子域不同的場景
降域的設置也是有限制的,只能把document.domain,設置成自身或者更高一級的域,且主域必須相同,如:a.b.test.com中的某個文檔的域可以設置成a.b.test.com、b.test.com、test.com。但是不可以設置成.com或者c.a.b.test.com或者baidu.com,因為baidu.com主域和當前域不同了。
使用方法
a頁面中加入document.domain = ‘test.com’;
b頁面中加入document.domain = ‘test.com’;
a.index.html
<div class="ct">
<h1>使用降域實現跨域</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新增的方法,可以實現跨文本檔、多窗口、跨域消息傳遞。該方法可以通過綁定window的message事件來監聽發送跨文檔消息傳輸內容。postMessage(data,origin)方法接受兩個參數:
1.data:要傳遞的數據。
2.origin:字符串參數,指明目標窗口的源,協議+主機+端口號[+URL],URL會被忽略,所以可以不寫,這個參數是為了安全考慮postMessage()方法只會將message傳遞給指定窗口,如果設置為"*",這樣可以傳遞給任意窗口。
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監聽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不是最新的數據格式,是json跨域獲取的解決方案,通過jsonp獲取的數據作為js的參數運行。瀏覽器有同源策略,會把跨域請求都禁止了,但是html的<script>標簽,不受同源策略的影響,可以從其他源獲取數據,所以我們可以通過script標簽,引入其他源的數據,通過js代碼進行解析。先定義處理函數functionname,然后通過添加一個<script>元素,src的地址是后端接口,在地址后加個請求參數callback=函數名,這個就是處理函數的函數名,向服務器請求JSON數據,服務器收到請求后,把數據放在回調函數中返回,返回的functionname(data)會放在script標簽作為js執行,所以會調用functionname(data),將data作為函數的參數。
CORS是什么
CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。相比JSONP只能發GET請求,CORS允許任何類型的請求。CORS需要瀏覽器和服務器同時支持,現代瀏覽器都支持此功能,IE瀏覽器要IE10以上。CORS的整個通信過程不需要用戶參與,瀏覽器會自己完成,CORS與AJAX的同源通信一樣,在發送AJAX請求的時候,瀏覽器會自動在頭部添加一個origin字段,只要服務器允許cors的約定,就可以實現跨域通信。
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。就是簡單請求,如果不滿足就是非簡單請求,
簡單請求
對于簡單請求,瀏覽器直接發cors請求,在頭部添加origin字段,origin字段用來說明本次請求來自哪個源,服務器會根據這個origin,決定是否同意通信,如果服務器不同意,也會返回正常的http響應,瀏覽器沒發現有origin這個字段,就是報錯,表示不能跨域通信。
** 非簡單請求**
非簡單請求是對服務器有特殊要求的請求,比如請求方法是PUT/DELETE,或者Content-Type字段的類型是application/json。
非簡單請求的CORS請求,會在正式itongxin之前,增加愛一次HTTP查詢請求,叫預檢請求(preflight)。
??瀏覽器現詢問服務器,當前網頁所在的域名是否在服務器的許可名單中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
除了Origin字段,預檢請求的頭信息還包括兩個特殊字段:
??(1). Access-Control-Request-Method
??該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,例子中是POST。
??(2). Access-Control-Request-Headers
??該字段是都好分割的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上面的例子默認application/json對應的額外字段是”Content-Type”。
服務器收到預檢請求后,檢查Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,確認允許跨源請求,并做出回應。
如果服務器否定了預檢請求,會返回一個正常的HTTP Response,但是沒有任何CORS相關的頭信息字段。這時候瀏覽器認為i額服務器不同意預檢請求,因此出發一個錯誤,被XMLHttpRequest對象的onerror毀掉函數捕獲。控制臺會打印出如下報錯信息。
一旦服務器通過了預檢請求,以后每次瀏覽器正常的CORS請求就都與簡單請求一樣,包括Origin字段信息。服務器的回應也會有Access-Control-Allow-Origin字段
根據視頻里的講解演示三種以上跨域的解決方式 ,寫成博客
最后一個cors的介紹參考了阮一峰老師的日志。