js跨域資源共享

斷了De弦發表於2018-08-07

同源策略

同源策略是瀏覽器的一個安全功能,不同源的客戶端指令碼在沒有明確授權的情況下,不能讀寫對方資源。只要協議、域名、埠有任何一個不同,都被當作是不同的域。

跨域資源共享

1、CORS

基本思想:使用自定義的HTTP頭部讓瀏覽器與伺服器進行溝通,從而決定請求或相應是否成功,還是失敗。

CORS原理:只需要向響應頭header中注入Access-Control-Allow-Origin,這樣瀏覽器檢測到header中的Access-Control-Allow-Origin,則就可以跨域操作了。如果沒有這個頭部或者有這個頭部但源資訊不匹配,瀏覽器就會駁回請求。

基本流程:

對於簡單請求,瀏覽器直接發出CORS請求。具體來說,就是在頭資訊之中,增加一個Origin欄位。

下面是一個例子,瀏覽器發現這次跨源AJAX請求是簡單請求,就自動在頭資訊之中,新增一個Origin欄位。


GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的頭資訊中,Origin欄位用來說明,本次請求來自哪個源(協議 + 域名 + 埠)。伺服器根據這個值,決定是否同意這次請求。

如果Origin指定的源,不在許可範圍內,伺服器會返回一個正常的HTTP迴應。瀏覽器發現,這個迴應的頭資訊沒有包含Access-Control-Allow-Origin欄位(詳見下文),就知道出錯了,從而丟擲一個錯誤,被XMLHttpRequestonerror回撥函式捕獲。注意,這種錯誤無法通過狀態碼識別,因為HTTP迴應的狀態碼有可能是200。

如果Origin指定的域名在許可範圍內,伺服器返回的響應,會多出幾個頭資訊欄位。


Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

上面的頭資訊之中,有三個與CORS請求相關的欄位,都以Access-Control-開頭。

(1)Access-Control-Allow-Origin

該欄位是必須的。它的值要麼是請求時Origin欄位的值,要麼是一個*,表示接受任意域名的請求。

(2)Access-Control-Allow-Credentials

該欄位可選。它的值是一個布林值,表示是否允許傳送Cookie。預設情況下,Cookie不包括在CORS請求之中。設為true,即表示伺服器明確許可,Cookie可以包含在請求中,一起發給伺服器。這個值也只能設為true,如果伺服器不要瀏覽器傳送Cookie,刪除該欄位即可。

(3)Access-Control-Expose-Headers

該欄位可選。CORS請求時,XMLHttpRequest物件的getResponseHeader()方法只能拿到6個基本欄位:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他欄位,就必須在Access-Control-Expose-Headers裡面指定。上面的例子指定,getResponseHeader('FooBar')可以返回FooBar欄位的值。

需要注意的是,如果要傳送Cookie,Access-Control-Allow-Origin就不能設為星號,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用伺服器域名設定的Cookie才會上傳,其他域名的Cookie並不會上傳,且(跨源)原網頁程式碼中的document.cookie也無法讀取伺服器域名下的Cookie。

2、影象Ping

       影象Ping跨域請求技術是使用<img>標籤。一個網頁可以從任何網頁中載入影象,不用擔心跨域不跨域。這也是線上廣告跟蹤瀏覽量的主要方式。也可以動態地建立影象,使用它們的onload和onerror事件處理程式來確定是否接收到了響應

  動態建立影象經常用於影象Ping:影象Ping是與伺服器進行簡單、單向的跨域通訊的一種方式。 請求的資料是通過査詢字串形式傳送的,而響應可以是任意內容,但通常是畫素圖或204響應。通過影象Ping,瀏覽器得不到任何具體的資料,但通過偵聽load和error事件,它能知道響應是什麼時候接收到的

var img = new Image();
img.onload = img.onerror = function(){
    alert("Done!");
};
img.src = "test.html?sum=a";

  這裡建立了一個Image的例項,然後將onload和onerror事件處理程式指定為同一個函式。這樣無論是什麼響應,只要請求完成,就能得到通知。請求從設定src屬性那一刻開始,而這個例子在請求中傳送了一個sum引數

影象Ping有兩個主要的缺點,一是隻能傳送GET請求,二是無法訪問伺服器的響應文字。因此,影象Ping只能用於瀏覽器與伺服器間的單向通訊。

3、JSONP

JSONP:是JSON  with padding(填充式JSON或引數式JSON)的簡寫,它由兩部分組成:回撥函式和資料。回撥函式是當響應到來時應該在頁面中呼叫的函式,回撥函式的名字一般是在請求中指定的,而資料就是傳入回撥函式中的JSON資料。

原理:如果一個頁面載入了一個外來的JS檔案,瀏覽器就會自動執行這個檔案中的程式碼。

JSONP是通過動態<script>元素來是用的,使用時可以為src屬性指定一個跨域URL。因為JSONP是有效的js程式碼,所以在請求完成後,即在JSONP相應載入到頁面中以後,就會立即執行。

JSONP之所以在開發人員中極為流行,主要是原因是它非常簡單易用。與影象Ping相比,它的優點在於能夠直接訪問相應文字,支援在瀏覽器與伺服器之間雙向通訊。例項:

前端:

<!DOCTYPE html>
<head>
    <title>jsonp</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<script>
    //動態建立script標籤,並請求
    function addScriptTag(src){
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.src = src;
        document.body.appendChild(script);
    };
    //在onload後,跨域請求
    window.onload = function(){
        addScriptTag('http://127.0.0.1:8080/jsonp?callback=test');
    };
    //回撥函式,必須為全域性,不然會報錯
    function test(data){
        alert(data.name);
    };
</script>
</body>
</html>

sever:

//告訴Node.js引入http模組給該伺服器應用使用
var http = require('http');
//引入url模組解析url字串
var url = require('url');
//引入querystring模組處理query字串
var querystring = require('querystring');
//建立新的HTTP伺服器
var server = http.createServer();
//通過request事件來響應request請求
server.on('request',function(req, res){
    var urlPath = url.parse(req.url).pathname;
    var qs = querystring.parse(req.url.split('?')[1]);
    if(urlPath === '/jsonp' && qs.callback){
        res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});
        var data = {
            "name": "Monkey"
        };
        data = JSON.stringify(data);
        var callback = qs.callback+'('+data+');';
        res.end(callback);
    }
    else{
        res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});
        res.end('Hell World\n');
    }
});
//監聽8080埠
server.listen('8080');
//用於提示我們伺服器啟動成功
console.log('Server running!');

缺點:

1.JSONP是從其他域中載入程式碼執行。如果其他域不安全,則可能會夾雜惡意程式碼。

2.要確定JSONP請求失敗並不容易。

3.只能使用get方式進行提交,不適合資料量較大請求。

4、Comet

Comet是一種伺服器向頁面推送資料的技術,兩種實現方式:長輪詢和流。

5、SSE

SSE是圍繞制度Comet互動推出的API或者模式,SSE API用於建立到伺服器的單向連線,伺服器通過這個連線可以傳送任意數量的資料。

6、Web Sockets(在獨立持久連線上提供全雙工、雙向通訊)

使用標準的HTTP伺服器無法實現,只有支援這種協議的專門伺服器才能正常工作。

 

 

 

相關文章