深刻淺出FE(三)跨域Cross-Origin

目錄css


1. 跨域是什麼html

2. 爲何有跨域前端

2.1 防止csrf攻擊java

2.2 防止xss攻擊node

3. 跨域解決方案?webpack

3.1 jsonpios

3.2 "跨域資源共享"(Cross-origin resource sharing)CROSnginx

3.3 document.domain + iframe跨域web

3.4 window.name + iframeajax

3.5 location.hash + iframe跨域

3.6 window.postmessage

3.7 websocket

3.8 nginx代理(來自前端常見跨域解決方案(全))

3.9 中間件代理(其餘語言同理)

四、拓展

五、參考資料

1. 跨域是什麼

介紹跨域以前,先來了解同源策略。同源策略就是瀏覽器爲了保證用戶信息的安全,防止惡意的網站竊取數據,禁止不一樣域之間的JS進行交互。對於瀏覽器而言只要域名、協議和端口其中一個不一樣就會引起同源策略,從而限制他們之間以下的交互行爲。

瀏覽器的同源策略會致使跨域,即兩個頁面只要域名、協議和端口三者任何一個不一致,請求都會跨域。

2. 爲何有跨域

跨域是爲了防止csrf和xss攻擊。

2.1 防止csrf攻擊

(1)當用戶訪問銀行www.bank.com,登錄並操做,這時用戶憑證如cookie等都生成並存放在瀏覽器;

(2)此時用戶又訪問了另外一個釣魚網站,這時該釣魚網站網站就能夠在它的頁面中,拿到銀行的cookie,好比用戶名,登錄token等,而後發起對www.bank.com的操做;

(3)若是這時瀏覽器若是沒有同源策略,銀行也沒有作響應的安全處理,那麼用戶就會被攻擊。

2.2 防止xss攻擊

xss攻擊包括三種類型-存儲型、反射型和DOM型,這裏主要是DOM型攻擊。

(1)有攻擊者製做了一個釣魚網站,這個網站內部嵌了一個和www.bank.com外觀一致的iframe;

(2)此時用戶進來後忽視了url不是銀行的的頁面,直接輸入帳戶名和密碼;

(3)若是這時瀏覽器若是沒有同源策略,那麼這些敏感信息就會被黑客獲取,銀行也沒有作相應的處理,那麼用戶就會被攻擊。

3. 跨域解決方案?

3.1 jsonp

3.1.1 原理

jsonp 是一種數據調用的方式。利用<script>標籤沒有跨域限制的「漏洞」。

3.1.2 用法

主要有兩種方式,一種是寫在url中,或者是動態生成script標籤,並插入到dom中,

當須要和後端通信時,本站腳本建立一個<script>元素,地址指向第三方的API網址,形如:

寫在url中

<script src="http://querydata.com/jsonp/api/defalt?callback=querydata;"></script>複製代碼

動態生成script,插入到dom中

// 建立一個腳本,而且告訴後端回調函數名叫querydata
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.type = 'text/javasctipt';
script.src = 'demo.js?callback=querydata';
body.appendChild(script);複製代碼


在本地提供一個回調函數querydata來接收數據(函數名可約定,或經過地址參數傳遞)。

function querydata(data){
    console.log(data)
}複製代碼

產生的響應爲json數據的包裝(故稱之爲jsonp,即json padding),形如:

callback({"name":"coolsummer","gender":"Male"})複製代碼

這樣瀏覽器會調用querydata函數,並傳遞解析後json對象做爲參數。本站腳本可在callback函數裏處理所傳入的數據。

3.1.3 優缺點及使用場景
3.1.3.1 jsonp 只能使用get請求,不能用於其餘類型的請求。

3.1.3.2 沒有關於 JSONP 調用的錯誤處理。

若是動態腳本插入有效,就執行調用;若是無效,就靜默失敗。失敗是沒有任何提示的。例如,不能從服務器捕捉到 404 錯誤,也不能取消或從新開始請求。不過,等待一段時間尚未響應的話,就不用理它了。(將來的 jQuery 版本可能有終止 JSONP 請求的特性)。

3.1.3.3 JSONP 被不信任的服務使用時會很危險。

由於 JSONP 服務返回打包在函數調用中的 JSON 響應,而函數調用是由瀏覽器執行的,這使宿主 Web 應用程序更容易受到各種攻擊。若是打算使用 JSONP 服務,瞭解它能形成的威脅很是重要。

3.1.3.4 拓展(來自:jsonp的工做原理):

  • html標籤的src屬性沒有同源限制(支持跨域),瀏覽器解析script標籤時,會自動下載src屬性值(url)指向的資源;
  • script標籤指向的資源文件被下載後,其中的內容會被當即執行
  • 服務器端的程序會解析src屬性值中的url傳遞的參數,根據這些參數針對性返回一個/多個函數調用表達式,這些函數調用表達式的參數就是客戶端跨域想獲得的數據
  • 服務器生成、返回的文件中,表達式調用的函數是已經在本地提早定義好的,而參數就是但願從跨域服務器拿到的數據。
  • 字面的script標籤能夠,動態添加到dom樹中的script也能夠,後者更方便綁定事件。

因此只要擁有」src」這個屬性的標籤都擁有跨域的能力,相似的能夠跨域內嵌資源的還有:

(1)<script src=""></script>標籤嵌入跨域腳本。語法錯誤信息只能在同源腳本中捕捉到。上面jsonp也用到了呢。

(2) <link src="">標籤嵌入CSS。因爲CSS的鬆散的語法規則,CSS的跨域須要一個設置正確的Content-Type消息頭。不一樣瀏覽器有不一樣的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。

(3)<video> 和 <audio>嵌入多媒體資源。

(4)<object>, <embed> 和 <applet>的插件。

(5)@font-face引入的字體。一些瀏覽器容許跨域字體( cross-origin fonts),一些須要同源字體(same-origin fonts)。

(6) <frame> 和 <iframe>載入的任何資源。站點可使用X-Frame-Options消息頭來阻止這種形式的跨域交互。

(7) image

圖像ping是與服務器進行簡單、單向的跨域通訊的一種方式,請求的數據是經過查詢字符串的形式發送的,而相應能夠是任意內容,但一般是像素圖或204相應(No Content)。 圖像ping有兩個主要缺點:首先就是隻能發送get請求,其次就是沒法訪問服務器的響應文本。

var img = new Image();
img.onload = img.onerror = function(){
    alert("done!");
};
img.src = "跨域資源路徑";
document.body.insertBefore(img,document.body.firstChild);複製代碼

3.2 "跨域資源共享"(Cross-origin resource sharing)CROS

3.2.1 使用場景及優缺點

目前,全部瀏覽器都支持該功能(IE8+:IE8/9須要使用XDomainRequest對象來支持CORS),CORS也已經成爲主流的跨域解決方案。

整個CORS通訊過程,都是瀏覽器自動完成,不須要用戶參與。對於開發者來講,CORS通訊與同源的AJAX通訊沒有差異,代碼徹底同樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感受。

CORS與JSONP的使用目的相同,可是比JSONP更強大。JSONP只支持GET請求,CORS支持全部類型的HTTP請求。JSONP的優點在於支持老式瀏覽器,以及能夠向不支持CORS的網站請求數據。

3.2.2 原理

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。

MDN解釋:

跨域資源共享(CORS)是一種機制,該機制使用附加的HTTP標頭來告訴瀏覽器以使Web應用程序在一個來源運行,並從另外一個來源訪問選定的資源。Web應用程序請求其來源(域,協議或端口)不一樣的資源時,將執行跨域HTTP請求

3.2.3 用法

實現CORS通訊的關鍵是服務器。只要服務器實現了CORS接口,就能夠跨源通訊。普通跨域請求只服務端設置Access-Control-Allow-Origin便可,前端無須設置.

若要帶cookie請求:先後端都須要設置。因爲同源策略的限制,所讀取的cookie爲跨域請求接口所在域的cookie,而非當前頁。

瀏覽器請求分爲兩種:簡單請求和非簡單請求。

3.2.3.1 簡單請求

對於簡單請求,瀏覽器直接發出CORS請求。

具體來講,就是在頭信息之中,增長一個Origin字段。Origin字段用來講明本次請求來自哪一個源(協議 + 域名 + 端口)。服務器根據這個值,決定是否贊成此次請求。而且origin中的值必須在後端設置的Access-Control-Allow-Origin的值中,若是後端設置了Access-Control-Allow-Origin這個屬性爲‘*’,即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...複製代碼

1)當origin不是服務端設置的Access-Control-Allow-Origin屬性中的值,服務器會返回一個正常的http響應,這時瀏覽器發現返回的response中沒有包含Access-Control-Allow-Origin字段,那麼會拋出錯誤,這個錯誤能夠被xhr的onerror回調函數捕獲,可是這個錯誤沒法經過http狀態碼識別,由於可能狀態碼爲200.

2)當origin是服務端設置的Access-Control-Allow-Origin屬性的值(或者Access-Control-Allow-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複製代碼
  • Access-Control-Allow-Origin 服務端設置的Access-Control-Allow-Origin屬性的值(或者Access-Control-Allow-Origin屬性設置爲*,即容許全部的請求,這樣一般會致使更多的麻煩,好比對接口的攻擊等,通常設置爲指定的域);
  • Access-Control-Allow-Credentials 可選布爾值,服務端是否發送cookie,默認狀況下,Cookie不包括在CORS請求之中,即此值爲false;此時若是想要在請求中帶上cookie,那麼前端須要設置帶上cookie的字段withCredentials字段。

以xhr、ajax或者axios請求爲例:

xhr

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;複製代碼

ajax

$.ajax({
        url: "http://localhost:9000",
        type: 'GET',
        xhrFields: {
            withCredentials: true // 這裏設置了withCredentials
        },
        success: function(data) {
            console.log(data)
        },
        error: function(err) {
            console.error(err)
        }
    })複製代碼

axios

axios.defaults.withCredentials=true複製代碼

服務端也要設置這個屬性爲true,不一樣語言設置方式各有不一樣,在此不一一舉例

Access-Control-Allow-Credentials: true複製代碼

注意:前端和服務端必須同時設置,不然兩端任意一端設置,另外一端不設置則不會攜帶cokie。可是省略withCredentials設置,有的瀏覽器仍是會一塊兒發送Cookie。這時,能夠顯式關閉withCredentials。

xhr.withCredentials = false;複製代碼
  • 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。

簡單請求是知足如下全部條件的請求(如下內容來自MDN:跨域資源共享(CORS),稍有修改):

注意:這些與Web內容已經能夠發出的跨站點請求種類相同,除非服務器發送適當的標頭,不然不會向請求者釋放響應數據。所以,能夠防止跨站點請求僞造的站點沒必要擔憂HTTP訪問控制。

注: WebKit每日和Safari瀏覽器技術預覽放置在容許的值的額外限制AcceptAccept-LanguageContent-Language頭。若是這些標頭中的任何一個具備「非標準」值,則WebKit / Safari不會將請求視爲「簡單請求」。沒有記錄WebKit / Safari認爲「非標準」的值,如下WebKit錯誤除外:

沒有其餘瀏覽器實現這些額外的限制,由於它們不是規範的一部分。

3.2.3.2 非簡單請求

非簡單請求會在簡單請求發送以前,增長一次「預檢」請求。

「簡單請求」即不須要「預檢」,「預檢」請求首先經過OPTIONS方法將HTTP請求發送到另外一個域上的資源,以肯定實際請求是否能夠安全發送。跨站點請求這樣被預檢,由於它們可能會影響用戶數據。

瀏覽器在發送請求前只要不是簡單請求,就會先發一個預檢請求檢測請求是否被服務器接受,好比域名是否在服務器所容許的白名單中,以及可使用哪些HTTP動詞和頭信息字段。

一個常見的預檢請求頭以下所示:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...複製代碼

方法必須是OPTIONS

除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。

  • Access-Control-Request-Method告知服務器實際請求所使用的 HTTP 方法。
  • Access-Control-Request-Headers告知服務器實際請求所攜帶的自定義首部字段。該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段

服務器基於從預檢請求得到的信息來判斷,是否接受接下來的實際請求。

服務器收到"預檢"請求之後,檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段之後,確認容許跨源請求,就能夠作出迴應---是否容許跨域訪問。

//來自MDN
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2019 01:15:39 GMT 
Server: Apache/2.0.61 (Unix) 
Access-Control-Allow-Origin: http://foo.example 
Access-Control-Allow-Methods: POST, GET, OPTIONS 
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type 
Access-Control-Max-Age: 86400 
Vary: Accept-Encoding, Origin 
Content-Encoding: gzip 
Content-Length: 0 
Keep-Alive: timeout=2, max=100 
Connection: Keep-Alive 
Content-Type: text/plain複製代碼

上面一段代碼是常見的預檢請求返回內容,最關鍵是Access-Control-Allow-Origin這個字段,表示http://api.bob.com能夠請求數據。該字段也能夠設爲星號,表示贊成任意跨源請求。

若是瀏覽器否認了"預檢"請求,會返回一個正常的HTTP迴應,可是沒有任何CORS相關的頭信息字段。這時,瀏覽器就會認定,服務器不一樣意預檢請求,所以觸發一個錯誤,XMLHttpRequest對象的onerror回調函數捕獲。控制檯會打印出以下的報錯信息。

XMLHttpRequest cannot load http://api.alice.com.
Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.複製代碼

服務器迴應的其餘CORS相關字段以下:

Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Credentials: true
Access-Control-Max-Age: 1728000複製代碼
  • Access-Control-Allow-Methods它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。注意,和請求的Access-Control-Allow-Method字段區別,請求的Access-Control-Allow-Method字段是單個的,表示檔次請求的方法,是單個方法,而服務端響應的Access-Control-Allow-Methods返回的是全部支持的方法,而不單是瀏覽器請求的那個方法。這是爲了不屢次"預檢"請求。
  • Access-Control-Allow-Headers若是瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在"預檢"中請求的字段。
  • Access-Control-Allow-Credentials該字段與簡單請求時的含義相同,是否攜帶cookie。
  • Access-Control-Max-Age該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即容許緩存該條迴應1728000秒(即20天),在此期間,不用發出另外一條預檢請求。

服務端經過預檢請求後:

一旦服務器經過了"預檢"請求,之後每次瀏覽器正常的CORS請求,就都跟簡單請求同樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。

下面是"預檢"請求以後,瀏覽器的正常CORS請求。

PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...複製代碼

上面頭信息的Origin字段是瀏覽器自動添加的。

下面是服務器正常的迴應。

Access-Control-Allow-Origin: http://api.bob.com
Content-Type: text/html; charset=utf-8複製代碼

上面頭信息中,Access-Control-Allow-Origin字段是每次迴應都一定包含的。

3.3 document.domain + iframe跨域

3.3.1 使用場景及優缺點

對於主域名相同,而子域名不一樣的狀況,可使用document.domain來跨域 這種方式很是適用於iframe跨域的狀況。

缺點是隻能用於iframe,並且若是兩個頁面通訊必須經過第三個頁面,而且傳遞值只能是單向。

MDN解釋:

Document 接口的 domain 屬性獲取/設置當前文檔的原始域部分,經常使用於同源策略

3.3.2 原理

兩個頁面都經過js強制設置document.domain爲基礎主域,就實現了同域。

3.3.3 用法(來自前端常見跨域解決方案(全)

1.)父窗口:(www.a.com/a.html)

<iframe id="iframe" src="http://child.a.com/b.html"></iframe>
<script>
    document.domain = 'a.com';
    var user = 'admin';
</script>複製代碼

2.)子窗口:(child.a.com/b.html)

<script>
    document.domain = 'a.com';
    // 獲取父窗口中變量
    alert('get js data from parent ---> ' + window.parent.user);
</script>複製代碼

3.4 window.name + iframe

3.4.1 使用場景及優缺點

瀏覽器窗口有window.name屬性。這個屬性的最大特色是,不管是否同源,只要在同一個窗口裏,前一個網頁設置了這個屬性,後一個網頁能夠讀取它。而且能夠支持很是長的 name 值(2MB)。

這種方法的優勢是,window.name容量很大,能夠放置很是長的字符串;缺點是必須監聽子窗口window.name屬性的變化,影響網頁性能。

3.4.2 原理

經過iframe的src屬性由外域轉向本地域,跨域數據即由iframe的window.name從外域傳遞到本地域。這個就巧妙地繞過了瀏覽器的跨域訪問限制,但同時它又是安全操做。

3.4.3 用法

父窗口先打開一個子窗口,載入一個不一樣源的網頁,該網頁將信息寫入window.name屬性。

window.name = data;複製代碼

接着,子窗口跳回一個與主窗口同域的網址。

location = 'http://parent.url.com/xxx.html';複製代碼

而後,主窗口就能夠讀取子窗口的window.name了。

var data = document.getElementById('myFrame').contentWindow.name;複製代碼

3.5 location.hash + iframe跨域

3.5.1 實現原理及優缺點

a與b跨域相互通訊,經過中間頁c來實現(且c與a是同域)。 三個頁面,不一樣域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通訊。

3.5.2 用法

A域:a.html -> B域:b.html -> A域:c.html,a與b不一樣域只能經過hash值單向通訊,b與c也不一樣域也只能單向通訊,但c與a同域,因此c可經過parent.parent訪問a頁面全部對象。

嵌套關係爲: a.html中嵌套iframe(b.html),b.html中嵌套iframe(c.html),c中能夠得到a.html的對象。

1.)a.html:(www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 向b.html傳hashsetTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);
    
    // 開放給同域c.html的回調方法
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    }
</script>複製代碼

2.)b.html:(www.domain2.com/b.html)

<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>
<script>
    var iframe = document.getElementById('iframe');

    // 監聽a.html傳來的hash值,再傳給c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    };
</script>複製代碼

3.)c.html:(www.domain1.com/c.html)

<script>
    // 監聽b.html傳來的hash值
    window.onhashchange = function () {
        // 再經過操做同域a.html的js回調,將結果傳回
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    };
</script>複製代碼

3.6 window.postmessage

3.6.1 使用場景和優缺點

a) 頁面和其打開的新窗口的數據傳遞

b) 多窗口之間消息傳遞

c) 頁面與嵌套的iframe消息傳遞

d) 上面三個場景的跨域數據傳遞

優勢是公共的標準,使用簡單,缺點是用於接收消息的頁面可能存在跨站安全問題,由於沒法檢查origin和source屬性會致使跨站點腳本攻擊。

這個應該就是之後解決dom跨域通用方法了

3.6.2 原理

API爲window對象新增了一個window.postMessage方法,容許跨窗口通訊,不論這兩個窗口是否同源。

3.6.3 用法(來自MDN)

/*
 * A窗口的域名是<http://example.com:8080>,如下是A窗口的script標籤下的代碼:
 */

var popup = window.open(...popup details...);

// 若是彈出框沒有被阻止且加載完成

// 這行語句沒有發送信息出去,即便假設當前頁面沒有改變location(由於targetOrigin設置不對)
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// 假設當前頁面沒有改變location,這條語句會成功添加message到發送隊列中去(targetOrigin設置對了)
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  // 咱們能相信信息的發送者嗎?  (也許這個發送者和咱們最初打開的不是同一個頁面).
  if (event.origin !== "http://example.org")
    return;

  // event.source 是咱們經過window.open打開的彈出頁面 popup
  // event.data 是 popup發送給當前頁面的消息 "hi there yourself! the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);複製代碼
/*
 * 彈出頁 popup 域名是<http://example.org>,如下是script標籤中的代碼:
 */

//當A頁面postMessage被調用後,這個function被addEventListenner調用
function receiveMessage(event)
{
  // 咱們能信任信息來源嗎?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source 就當前彈出頁的來源頁面
  // event.data 是 "hello there!"

  // 假設你已經驗證了所受到信息的origin (任什麼時候候你都應該這樣作), 一個很方便的方式就是把event.source
  // 做爲回信的對象,而且把event.origin做爲targetOrigin
  event.source.postMessage("hi there yourself! the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);複製代碼

注意:任何窗口能夠在任何其餘窗口訪問此方法,在任什麼時候間,不管文檔在窗口中的位置,向其發送消息。 所以,用於接收消息的任何事件監聽器必須首先使用origin和source屬性來檢查消息的發送者的身份。 這不能低估:沒法檢查origin和source屬性會致使跨站點腳本攻擊。

3.7 websocket

3.7.1 應用場景及優缺點

咱們知道websocket是H5爲了解決http協議單項請求的缺陷。雖然能夠用輪訓解決-每隔一段時間查詢狀態變化,可是這種方式存在延時且對服務端形成很大負載。

WebSocket 協議在2008年誕生,2011年成爲國際標準。全部瀏覽器都已經支持了。

websocket最大的特色就是服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息,是真正的雙向平等對話,屬於服務器推送技術的一種。

websocket的特色:

(1)創建在 TCP 協議之上,服務器端的實現比較容易。

(2)與 HTTP 協議有着良好的兼容性。默認端口也是80和443,而且握手階段採用 HTTP 協議,所以握手時不容易屏蔽,能經過各類 HTTP 代理服務器。

(3)數據格式比較輕量,性能開銷小,通訊高效。

(4)能夠發送文本,也能夠發送二進制數據。

(5)沒有同源限制,客戶端能夠與任意服務器通訊。

(6)協議標識符是ws(若是加密,則爲wss),服務器網址就是 URL。

3.7.2 原理

WebSocket是一種通訊協議,使用ws://(非加密)和wss://(加密)做爲協議前綴。該協議不實行同源政策,只要服務器支持,就能夠經過它進行跨源通訊。

3.7.3 用法

以下是websocket的一個簡單的用法

var ws = new WebSocket("wss://echo.websocket.org");

ws.onopen = function(evt) { 
  console.log("Connection open ..."); 
  ws.send("Hello WebSockets!");
};

ws.onmessage = function(evt) {
  console.log( "Received Message: " + evt.data);
  ws.close();
};

ws.onclose = function(evt) {
  console.log("Connection closed.");
};      複製代碼

原生WebSocket API使用起來不太方便,可使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。本此由於是模擬就沒有安裝了用了WebSocket。

3.8 nginx代理(來自前端常見跨域解決方案(全))

一、 nginx配置解決iconfont跨域

瀏覽器跨域訪問js、css、img等常規靜態資源被同源策略許可,但iconfont字體文件(eot|otf|ttf|woff|svg)例外,此時可在nginx的靜態資源服務器中加入如下配置。

location / {
  add_header Access-Control-Allow-Origin *;
}複製代碼

二、 nginx反向代理接口跨域

跨域原理: 同源策略是瀏覽器的安全策略,不是HTTP協議的一部分。服務器端調用HTTP接口只是使用HTTP協議,不會執行JS腳本,不須要同源策略,也就不存在跨越問題。

實現思路:經過nginx配置一個代理服務器(域名與domain1相同,端口不一樣)作跳板機,反向代理訪問domain2接口,而且能夠順便修改cookie中domain信息,方便當前域cookie寫入,實現跨域登陸。

nginx具體配置:

#proxy服務器
server {
    listen       81;
    server_name  www.domain1.com;

    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie裏域名
        index  index.html index.htm;

        # 當用webpack-dev-server等中間件代理接口訪問nignx時,此時無瀏覽器參與,故沒有同源限制,下面的跨域配置可不啓用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #當前端只跨域不帶cookie時,可爲*
        add_header Access-Control-Allow-Credentials true;
    }
}複製代碼

1.) 前端代碼示例:

var xhr = new XMLHttpRequest();

// 前端開關:瀏覽器是否讀寫cookie
xhr.withCredentials = true;

// 訪問nginx中的代理服務器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();複製代碼

2.) Nodejs後臺示例:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
    var params = qs.parse(req.url.substring(2));

    // 向前臺寫cookie
    res.writeHead(200, {
        'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:腳本沒法讀取
    });

    res.write(JSON.stringify(params));
    res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');複製代碼

3.9 中間件代理(其餘語言同理)

node中間件實現跨域代理,原理大體與nginx相同,都是經過啓一個代理服務器,實現數據的轉發,也能夠經過設置cookieDomainRewrite參數修改響應頭中cookie中域名,實現當前域的cookie寫入,方便接口登陸認證。

四、拓展

1.注意本文主要討論前端的CROS,關於服務端的CROS可參考:服務器端訪問控制(CORS)

2.經過離線緩存,如localhost和indexdb也可以跨域

3.阮一峯文章瀏覽器同源政策及其規避方法中提到的片斷識別碼是一種不太常見的方式。

五、參考資料

1.跨域資源共享 CORS 詳解

2.jsonp的工做原理

3.結合 JSONP 和 jQuery 快速構建強大的 mashup

4.跨域資源共享 CORS 詳解

5.跨域資源共享(CORS)

6.Document.domain

7.前端常見跨域解決方案(全)

8.SendMessage、PostMessage原理

9.WebSocket 教程

10.瀏覽器同源政策及其規避方法

相關文章
相關標籤/搜索