瀏覽器同源政策之ajax請求不能傳送
同源政策(same-origin policy)是瀏覽器安全的基石
同源指的是三個相同
限制範圍
(1) Cookie、LocalStorage 和 IndexDB 無法讀取。
(2) DOM 無法獲得。
(3) AJAX 請求不能傳送。
下面重點講解AJAX 請求不能傳送:
同源政策規定,AJAX請求只能發給同源的網址,否則就報錯。
有4種方法規避這個限制:
1.JSONP
2.WebSocket
3.CORS
4.架設伺服器代理
1.JSONP
它的基本思想是,網頁通過新增一個<script>
元素,向伺服器請求JSON資料,這種做法不受同源政策限制(只限制3種,上面有說);一個要點就是允許使用者傳遞一個callback引數給服務端,然後服務端返回資料時會將這個callback引數作為函式名來包裹住JSON資料
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('Your public IP address is: ' + data.ip);
};
侷限性和安全性
1.JSONP僅適用於http的get請求。只能使用GET請求就意味著很多限制,比如:nginx伺服器預設的限制是4K或者8K,這是根據伺服器的硬體配置有關的,一般為記憶體一頁的大小,目前大部分為4K,即4096位元組
2.JSONP缺乏錯誤處理機制,如果指令碼注入成功後,就會呼叫回撥函式,但是注入失敗後,沒有任何提示。
3.在安全方面,藉助JSONP有可能進行跨站請求偽造(CSRF)攻擊,當一個惡意網站使用訪問者的瀏覽器向伺服器傳送請求並進行資料變更時,被稱為CSRF攻擊。由於請求會攜帶cookie資訊,伺服器會認為是使用者自己想要提交表單或者傳送請求,而得到使用者的一些隱私資料
2.WebSocket
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才沒有實行同源政策。因為伺服器可以根據這個欄位,判斷是否許可本次通訊。如果該域名在白名單內,伺服器就會做出如下回應。
除
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
3.CORS
CORS是一個W3C標準,全稱是”跨域資源共享”(Cross-origin resource sharing)。它允許瀏覽器向跨源(協議 + 域名 + 埠)伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。相比JSONP只能發GET請求,CORS允許任何型別的請求。
實現CORS通訊的關鍵是伺服器。只要伺服器實現了CORS介面,就可以跨源通訊
基本流程
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和非簡單請求(not-so-simple request)。
簡單請求:只需要在頭資訊之中增加一個Origin欄位。
非簡單請求:會在正式通訊之前,增加一次HTTP查詢請求,稱為”預檢”請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
只要同時滿足以下兩大條件,就屬於簡單請求。
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
nodejs下的實現方法:
router.get('/getData', function(req, res, next) {
//設定允許跨域請求
var reqOrigin = req.header("origin");
res.header("Access-Control-Allow-Origin", "http://localhost:3000");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
res.json(200, {name:"張三1",age:40});
}
配置項解釋
Access-Control-Allow-Origin
該欄位必填。它的值要麼是請求時Origin欄位的具體值,要麼是一個*,表示接受任意域名的請求。
Access-Control-Allow-Methods
該欄位必填。它的值是逗號分隔的一個具體的字串或者*,表明伺服器支援的所有跨域請求的方法。注意,返回的是所有支援的方法,而不單是瀏覽器請求的那個方法。這是為了避免多次”預檢”請求。
Access-Control-Expose-Headers
該欄位可選。CORS請求時,XMLHttpRequest物件的getResponseHeader()方法只能拿到6個基本欄位:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他欄位,就必須在Access-Control-Expose-Headers裡面指定。
Access-Control-Allow-Credentials
該欄位可選。它的值是一個布林值,表示是否允許傳送Cookie.預設情況下,不發生Cookie,即:false。對伺服器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type欄位的型別是application/json,這個值只能設為true。如果伺服器不要瀏覽器傳送Cookie,刪除該欄位即可。
Access-Control-Max-Age
該欄位可選,用來指定本次預檢請求的有效期,單位為秒。在有效期間,不用發出另一條預檢請求。
注意:
a.所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10
b.CORS請求傳送Cookie時,Access-Control-Allow-Origin只能是與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用伺服器域名設定的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁程式碼中的document.cookie也無法讀取伺服器域名下的Cookie。
4.架設伺服器代理
瀏覽器請求同源伺服器,再由後者請求外部服務