一、跨域and同源政策
1.跨域,指的是瀏覽器不能執行其他網站的腳本。它是由瀏覽器的同源策略造成的,是瀏覽器對javascript施加的安全限制。
2.同源政策
- 協議相同
- 域名相同
- 端口相同
如:
如,http://www.example.com/dir/page.html
這個網址,協議是http://
,域名是www.example.com
,端口是80
(默認端口可以省略)。它的同源情況如下。
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
https://www.example.com/dir/page.html:不同源(協議不同)
如果非同源,共有三種行為受到限制。
(1) Cookie、LocalStorage 和 IndexedDB 無法讀取。
(2) DOM 無法獲得。
(3) AJAX 請求無效(可以發送,但瀏覽器會拒絕接受響應)。
二、如何解決跨域問題
1.cookie
舉例來說,A網頁是http://w1.example.com/a.html
,B網頁是http://w2.example.com/b.html
,那么只要設置相同的document.domain,兩個網頁就可以共享Cookie。
document.domain = 'example.com';
這種方法只適用于 Cookie 和 iframe窗口,LocalStorage 和 IndexedDB 無法通過這種方法,規避同源政策,而要使用PostMessage API。
2. frame
如果兩個窗口一級域名相同,只是二級域名不同,那么設置document.domain屬性,就可以規避同源政策,拿到DOM。
對于完全不同源的網站,目前有兩種方法,可以解決跨域窗口的通信問題。
- 片段識別符(fragment identifier)
URL的#號后面的部分,比如http://example.com/x.html#fragment
的#fragment
。如果只是改變片段標識符,頁面不會重新刷新。
父窗口可以把信息,寫入子窗口的片段標識符。
子窗口通過監聽hashchange事件得到通知。
子窗口也可以改變父窗口的片段標識符。 - 跨文檔通信API(Cross-document messaging)
window.postMessage
父窗口aaa.com
向子窗口bbb.com
發消息,調用postMessage。
var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');
postMessage方法的第一個參數是具體的信息內容,第二個參數是接收消息的窗口的源(origin),即“協議 + 域名 + 端口”。也可以設為*,表示不限制域名,向所有窗口發送。
子窗口向父窗口發送消息的寫法類似。
window.opener.postMessage('Nice to see you', 'http://aaa.com');
- message事件的事件對象event,提供以下三個屬性。
event.source:發送消息的窗口
event.origin: 消息發向的網址
event.data: 消息內容
window.addEventListener('message', receiveMessage);
function receiveMessage(event) {
event.source.postMessage('Nice to see you!', '*');
}
3.LocalStorage
通過window.postMessage,讀寫其他窗口的 LocalStorage 也成為了可能。
3.AJAX
同源政策規定,AJAX請求只能發給同源的網址,否則就報錯。
除了架設服務器代理(瀏覽器請求同源服務器,再由后者請求外部服務),有三種方法規避這個限制。
- JSONP
- WebSocket
- CORS
(1)JSONP是服務器與客戶端跨源通信的常用方法。最大特點就是簡單適用,老式瀏覽器全部支持,服務器改造非常小。
它的基本思想是,網頁通過添加一個<script>元素,向服務器請求JSON數據,這種做法不受同源政策限制;服務器收到請求后,將數據放在一個指定名字的回調函數里傳回來。
//網頁動態插入<script>元素,由它向跨源網址發出請求。
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
//向服務器example.com發出請求
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
//該請求的查詢字符串有一個callback參數,用來指定回調函數的名字,這對于JSONP是必需的。
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
//服務器收到這個請求以后,會將數據放在回調函數的參數位置返回。
foo({
"ip": "8.8.8.8"
});
(2)WebSocket是一種通信協議,使用ws://(非加密)和wss://(加密)作為協議前綴。該協議不實行同源政策,只要服務器支持,就可以通過它進行跨源通信。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
有一個字段是Origin,表示該請求的請求源(origin),即發自哪個域名。
正是因為有了Origin這個字段,所以WebSocket才沒有實行同源政策。因為服務器可以根據這個字段,判斷是否許可本次通信。
(3)CORS
跨源資源分享(Cross-Origin Resource Sharing)。相比JSONP只能發GET請求,CORS允許任何類型的請求。
允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。。
CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。
整個CORS通信過程,都是瀏覽器自動完成
兩種請求
簡單請求(simple request)
請求方法
HEAD
GET
POST
//HTTP頭信息
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三個值application/x-www-form-urlencoded、
multipart/form-data、text/plain
非簡單請求(not-so-simple request)
請求方法
PUT
DELETE
或者Content-Type字段的類型是application/json
。
- CORS與JSONP的使用目的相同,但是比JSONP更強大。
JSONP只支持GET請求,CORS支持所有類型的HTTP請求。JSONP的優勢在于支持老式瀏覽器,以及可以向不支持CORS的網站請求數據。