Web端即時通信基礎知識補課:一文搞懂跨域的全部問題!

本文原做者: Wizey,做者博客:wenshixin.gitee.io,即時通信網收錄時有改動,感謝原做者的無私分享。php

一、引言

典型的Web端即時通信技術應用場景,主要有如下兩種形式:html

  • 1)做爲完整的即時通信產品進行應用:好比獨立的Web端IM產品;
  • 2)做爲某個更大系統中的一部分進行應用:好比客服系統(至關於工單系統裏嵌入IM技術啦)。

對於第一種場景,爲了更好的劃分功能邏輯,一個完整的產品一般都會調用來自於不一樣服務器提供的各類接口(好比各類服務端微服務接口),那麼Web端跨域問題就沒法迴避了。前端

對於第二種場景,就更好理解:爲了提高系統的可維護性,不一樣子系統間代碼的互不傾入、低偶合設計,致使im子系統或服務極可能部署於獨立的一臺或多臺服務器(域名)上,那麼跨域問題顯而易見。jquery

因此,對於Web端即時通信開發者來講,跨域問題是必須掌握的知識範疇。本文將爲你講解跨域問題原理,以及理論聯繫實際,用實踐代碼也爲你演示解決跨域問題的幾種方法。git

PS:雖然在開發Web端即時通信應用時,普通的Ajax調用、iframe文件上傳等存在跨域問題,但好消息是做爲技術核心的 WebSocket 技術是支持跨域的(不存在跨域問題)!web

友情提示:本文配套的實踐代碼,請從文末附件處下載!ajax

學習交流:chrome

- 即時通信/推送技術開發交流5羣: 215477170[推薦]
- 移動端IM開發入門文章:《 新手入門一篇就夠:從零開發移動端IM

(本文同步發佈於:www.52im.net/thread-2732…
json

二、什麼是跨域問題

前端調用的後端接口不屬於同一個域(域名或端口不一樣),就會產生跨域問題,也就是說你的應用訪問了該應用域名或端口以外的域名或端口。小程序

通俗的講,跨域問題是由於瀏覽器的同源策略規定某域下的客戶端在沒明確受權的狀況下,不能讀寫另外一個域的資源。而在實際開發中,先後端經常是相互分離的,而且先後端的項目部署也經常不在一個服務器內或者在一個服務器的不一樣端口下。前端想要獲取後端的數據,就必須發起請求,若是不作一些處理,就會受到瀏覽器同源策略的約束。後端能夠收到請求並返回數據,可是前端沒法收到數據。

三、爲何會發生跨域問題

要同時知足三個條件纔會產生跨域問題:

  • 1)瀏覽器限制,而不是服務端限制,能夠查看Network,請求可以正確響應,response返回的值也是正確的;
  • 2)請求地址的域名或端口和當前訪問的域名或端口不同;
  • 3)發送的是XHR(XMLHttpRequest)請求,可使用 a 標籤(模擬xhr請求)和 img 標籤(模擬json請求)作對比(控制檯只報了一個跨域異常)。

關於 XMLHTTPRequest 能夠參看這篇文章 :《你真的會使用XMLHttpRequest嗎?》。

跨域問題的根本,就是瀏覽器制定的同源策略致使的。

瀏覽器制定同源策略,其中一個重要緣由就是對cookie的保護。

cookie 中存着sessionID 。黑客一旦獲取了sessionID,而且在有效期內,就能夠登陸。當咱們訪問了一個惡意網站 若是沒有同源策略 那麼這個網站就能經過js 訪問document.cookie 獲得用戶關於的各個網站的sessionID 其中可能有銀行網站 等等。經過已經創建好的session鏈接進行攻擊,好比CSRF攻擊。

這裏須要服務端配合再舉個例子,如今我扮演壞人 我經過一個iframe 加載某寶的登陸頁面 等傻傻的用戶登陸個人網站的時候 我就把這個頁面彈出用戶一看 阿里唉大公司 確定安全 就屁顛屁顛的輸入了密碼 注意 若是沒有同源策略 我這個惡意網站就能經過dom操做獲取到用戶輸入的值 從而控制該帳戶因此同源策略是絕對必要的。

還有須要注意的是同源策略沒法徹底防護CSRF(即(Cross-site request forgery)跨站請求僞造)。

四、解決跨域問題的三種思路

  • 1)客戶端瀏覽器解除跨域限制:此方式理論上能夠可是不現實;
  • 2)發送JSONP請求替代XHR請求:此種方式雖然有必定的侷限性——好比請求只能是GET方式,但對於部署來講很友好,由於不須要修改服務器配置;
  • 3)修改服務器端配置(包括HTTP服務器和應用服務器):此方式對於GET、POST請求來講,沒有侷限性,但對於部署來講不太友好,須要修改應用服務器、反向代理服務器的相關配置。

五、跨域問題解決方法1:設置瀏覽器解除跨域限制

瀏覽器默認都是開啓跨域安全檢查的,咱們可使用命令行啓動瀏覽器,加上禁止安全檢查的參數,以谷歌瀏覽器爲例,chrome.exe --disable-web-security --user-data-dir=E:/temp --user-data-dir 爲瀏覽器緩存臨時目錄,瀏覽器這時會提示安全問題。

【瀏覽器如何判斷一個請求是否是跨域請求?】

瀏覽器會根據同源策略來判斷一個請求是否是跨域請求。

非跨域請求:在請求頭中會只包含請求的主機名:

跨域請求:在請求頭中會既包含要請求的主機名還包括當前的源主機名,若是這二者不一致,那就是跨域請求了:

【瀏覽器對請求的分類】

在HTTP1.1 協議中的,請求方法分爲GET、POST、PUT、DELETE、HEAD、TRACE、OPTIONS、CONNECT 八種。瀏覽器根據這些請求方法和請求類型將請求劃分爲簡單請求和非簡單請求。

簡單請求:瀏覽器先發送(執行)請求而後再判斷是否跨域。

請求方法爲 GET、POST、HEAD,請求頭header中無自定義的請求頭信息,請求類型Content-Type 爲 text/plain、multipart/form-data、application/x-www-form-urlencoded 的請求都是簡單請求。

非簡單請求:瀏覽器先發送預檢命令(OPTIONS方法),檢查經過後才發送真正的數據請求。


預檢命令會發送自定義請求頭爲Access-Control-Request-Headers: content-type的請求到服務器,根據響應頭的中的 「Access-Control-Allow-Headers」: 「Content-Type」 判斷服務器是否容許跨域訪問。預檢命令是能夠緩存,服務器端設置 「Access-Control-Max-Age」: 「3600」,這樣後面發送一樣的跨域請求就不須要先發送預檢命令了。

請求頭的含義以下所示:

響應頭的含義以下所示:

請求方法爲 PUT、DELETE 的 AJAX 請求、發送 JSON 格式的 AJAX 請求、帶自定義頭的 AJAX 請求都是非簡單請求。

六、跨域問題解決方法2:使用JSONP替代XHR

6.1JSONP 是什麼

JSONP(JSON with Padding)是JSON的一種補充使用方式,不是官方協議,而是利用 Script 標籤請求資源能夠跨域的特色,來解決跨域問題的,是一種變通的解決方案。(詳見《詳解Web端通訊方式的演進:從Ajax、JSONP 到 SSE、Websocke》一文中的第3節「3、JSONP」)

6.2使用 JSONP,服務器後臺代碼須要改動嗎?

答案是須要,這裏以Spring Boot爲例,在 Spring Boot 1.5 大版本中,添加一個切面來支持JSONP請求。

AJAX代碼以下:
$.ajax({
 url: baseUrl + "/get1",
 dataType: "jsonp", // 關鍵字段
 jsonp: "callback", // 先後端默認的約定
 cache: true, // 表示請求結果能夠被緩存,url中不會有下劃線參數了
 success: function(json) {
 result = json;
 }
}); 
服務端代碼:
@ControllerAdvice
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
 public JsonpAdvice() {
 super("callback");
 }
}複製代碼

6.3JSONP 實現原理

JSONP請求的類型是JavaScript腳本(callback 做爲先後端的約定,callback的值作爲方法名,json內容做爲方法的參數),而XHR請求的類型是json類型。

▲ JSONP返回類型和XHR返回類型對比

能夠在瀏覽器中查看 Jquery 源碼來驗證 JSONP 是否將請求包裝成了 script 腳本。

▲ JSONP動態生成script標籤

在 Jquery 源碼中打斷點。

▲ 在jquery中打斷點

刷新後查看 element 元素,能夠看到 Jquery 在 html 源碼中添加了 script 標籤。

▲ jquery動態生成script腳本

6.4JSONP的優缺點

JSONP的優勢:部署時不須要應用服務器去進行額外的配置,跟普通的非跨域系統部署如出一轍,沒有特別的要求。

JSONP的缺點:

  • 1)只支持 GET 方法請求,無論 AJAX 中實際的請求方法是否是 GET;
  • 2)服務端還須要修改代碼(若是你認爲修改服務端代碼比修改服務器的配置相比,很煩的話,這卻是能夠算做是缺點);
  • 3)發送的不是 XHR 請求,沒法使用 XHR 對象(但這也是爲何能夠解決跨域問題的根本)。

七、跨域問題解決方法3:修改應用服務器的跨域配置

根據現現在網站架構設計,能夠將前端應用看做調用方使用服務,將後端應用看做被調用方提供服務。

根據服務器的做用,能夠將服務器分爲 HTTP 服務器和應用服務器,全部修改服務器端既能夠是修改應用服務器,也能夠是修改 HTTP 服務器。

7.1被調用方修改

被調用方的解決思路是在響應頭中增長指定的字段容許調用方服務器跨域調用。


在應用服務器增長指定字段:

對於不帶 Cookie 的跨域請求,設置容許跨域的原始域名爲任意域名,」Access-Control-Allow-Origin」: 「*「,設置容許跨域的方法爲任意方法,」Access-Control-Allow-Methods」: 「*「,可是這樣的星號設置不能知足帶 Cookie 的跨域請求。

對於帶 Cookie 的跨域請求,要指名容許跨域請求的調用方主機名,Cookie 要加在調用方。

帶自定義頭的跨域請求,設置容許跨域的請求頭自定義的請求頭,」Access-Control-Allow-Headers」:」自定義的請求頭」。

在 Java Web 中,能夠添加一個過濾器來設置上面的參數。


▲ 被調用方使用Filter解決跨域

而使用 Spring Boot 框架,只須要在 Controller 類上加上 @CrossOrigin 註解就能夠輕鬆解決跨域問題了。

在 HTTP 服務器增長指定字段:

以經常使用的 Nginx 服務器和 Apache 服務器爲例。

Nginx 服務器容許跨域配置(注意不要手動直接點擊Nginx.exe,不然中止和從新載入配置會失敗的):

Apache 服務器容許跨域配置:

7.2調用方修改

調用方的解決思路是反向代理,也便是將被調用方的域名代理到調用方域名下,這樣就符合同源策略了,也就解決了跨域問題。


▲ 調用方反向代理效果演示

調用方修改通常都是直接修改 HTTP 服務器配置。

Nginx 服務器反向代理配置:

Apache 服務器反向代理配置:

八、本文配套的代碼下載

請從連接:Web端即時通信基礎知識補課:一文搞懂跨域的全部問題!-網頁端IM開發/專項技術區 - 即時通信開發者社區! 末尾處附件中下載之。

附錄1:Web端即時通信技術入門文章提綱

Web即時通信新手入門貼:

新手入門貼:詳解Web端即時通信技術的原理

Web端即時通信技術盤點請參見:
Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE

關於Ajax短輪詢:
找這方面的資料沒什麼意義,除非忽悠客戶,不然請考慮其它3種方案便可。

有關Comet技術的詳細介紹請參見:

更多WebSocket的詳細介紹請參見:

有關SSE的詳細介紹文章請參見:
SSE技術詳解:一種全新的HTML5服務器推送事件技術

更多WEB即時通信文章請見:
www.52im.net/forum.php?m…

附錄2:更多有關WEB端即時通信開發的精華文章

Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE
SSE技術詳解:一種全新的HTML5服務器推送事件技術
socket.io實現消息推送的一點實踐及思路
LinkedIn的Web端即時通信實踐:實現單機幾十萬條長鏈接
Web端即時通信技術的發展與WebSocket、Socket.io的技術實踐
Web端即時通信安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)
開源框架Pomelo實踐:搭建Web端高性能分佈式IM聊天服務器
使用WebSocket和SSE技術實現Web端消息推送
詳解Web端通訊方式的演進:從Ajax、JSONP 到 SSE、Websocket
MobileIMSDK-Web的網絡層框架爲什麼使用的是Socket.io而不是Netty?
理論聯繫實際:從零理解WebSocket的通訊原理、協議格式、安全性
微信小程序中如何使用WebSocket實現長鏈接(含完整源碼)
八問WebSocket協議:爲你快速解答WebSocket熱門疑問
快速瞭解Electron:新一代基於Web的跨平臺桌面技術
一文讀懂前端技術演進:盤點Web前端20年的技術變遷史
Web端即時通信知識補課:一文搞懂跨域的全部問題!
>> 更多同類文章 ……

(本文同步發佈於:www.52im.net/thread-2732…

相關文章
相關標籤/搜索