一、背景
同源政策
同源政策可以了解為浏覽器的一種安全機制,浏覽器隻允許與本域下的接口進行互動。不同源的用戶端在沒有明确授權的情況下,不能讀寫服務端的資源。
什麼是不同源呢:
- 不同的協定 例:http/https
- 不同的域名
- 不同的端口
- 不同的二級域名 例:http://a.baidu.com、http://b.baidu.com(不然哪來的降域呢)
補充點
在出現跨域問題時,浏覽器究竟在哪一步進行了攔截?用戶端請求時?伺服器不做出響應?還是伺服器響應後浏覽器拒絕的響應??
image.png
image.png
測試發現,在用戶端和服務端不同源的情況下,服務端其實是有響應的,在此時是浏覽器的同源政策拒絕了響應,認為這樣不安全。
二、解決跨域問題
1. JSONP(JSON with Padding)
- 原理: 在HTML中用script标簽在加載其他域下的js,那麼JSON就是利用這一特性來請求服務端,進而避免了ajax請求出現的跨域問題
- 具體實施
(1) 定義資料處理函數_fun
(2) 建立script标簽,并添加src位址來請求服務端接口,在src尾端加上自定義參數callback=_fun
(3) 服務端收到請求後,根據get參數判斷傳回拼接的字元串:'_fun(data)'
(4) fun(data)會放到script标簽作為js執行,那麼此時就會調用fun函數,将data作為函數的參數
- 相應代碼
用戶端:
擷取天氣
function weather(data){
console.log(data)
}
document.querySelector('.btn').addEventListener('click', function(){
var script = document.createElement('script')
script.src = 'http://localhost:8080/getWeather?callback=weather&a=b'
document.head.appendChild(script)
document.head.removeChild(script)
})
服務端:
'/getWeather':function(req, resp){
resp.writeHead(200, 'success')
if(req.query.callback){
var res = JSON.stringify({'beijing':'sunny'})
resp.end(req.query.callback+'('+res+')')
}else{
resp.end(JSON.stringify({'beijing':'rain'}))
}
}
2. CORS(Cross-Origin Resource Sharing)跨域資源共享
CORS,跨域資源共享,是一種ajax跨域請求資源的方式。(幾乎所有浏覽器都支援,但是ie必須10以上。)
(1) 當用戶端使用XMLHttpRequest發送請求時,浏覽器發現請求不符合同源政策,會加上一個請求頭:origin;
擷取天氣
document.querySelector('.btn').addEventListener('click', function(){
var xhr = new XMLHttpRequest()
xhr.open('get', 'http://www.b.com:8080/getWeather', true)
xhr.onload = function(){
console.log(xhr.responseText)
}
xhr.send()
})
image.png
(2) 在服務端收到請求并響應時,設定在響應結果加上Access-Control-Allow-Origin;
'/getWeather':function(req, resp){
resp.setHeader('Access-Control-Allow-Origin','http://www.a.com:8080')
resp.writeHead(200, 'success')
resp.end(JSON.stringify({'beijing':'rain'}))
}
image.png
或者response.setHeader('Access-Control-Allow-Origin',''),‘’代表接收所有位址的請求。
(3) 此時浏覽器判斷如果該響應頭包含請求頭origin的值,就接收響應,否則拒絕就拿不到響應資料。
CORS與JSONP差別:
- CORS與JSONP的使用目的相同,但是比JSONP更強大;
- JSONP隻支援GET請求,CORS支援所有類型的HTTP請求;
- JSONP的優勢在于支援老式浏覽器,以及可以向不支援CORS的網站請求資料。
3. 降域
會發現當頁面嵌入iframe的時候,在不同源的二級域名情況下我們是不能通過js操作iframe裡面的内容的,這時可以通過降域達到目的。
使用降域實作跨域
使用降域實作跨域
//URL: http://a.test.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "test.com"
4. postMassage
在某些情況下iframe裡面的服務端是願意提供一些接口的,這時就用到了
postMessage(),postMessage()方法允許來自不同源的腳本采用異步方式進行有限的通信,可以實作跨文本檔、多視窗、跨域消息傳遞。
//URL:http://www.a.com:8080/a.html
window.frames[0].postMessage(data,'*')
//URL:http://www.b.com:8080/b.html
window.addEventListener('message',function(e){
console.log(e.data)
})
5. 服務端中轉跨域
如果對方伺服器不提供跨域支援怎麼辦呢?這是可以自己搭建server請求中轉(在服務端不存在同源政策限制)。
現在我有一個天氣預報頁面http://www.a.com,需要向http://www.weather.com接口擷取天氣資料,但是這個接口不支援JSONP和CORS跨域,那麼可以這樣做:
- 搭建伺服器,建立一個擷取天氣的接口http://www.b.com
- 設定接口http://www.b.com的響應頭Access-Control-Allow-Origin:http://www.a.com
- 頁面http://www.a.com向http://www.b.com發請求
- http://www.b.com收到請求後,向http://www.weather.com擷取天氣資料,然後将天氣資料傳回給http://www.a.com