Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 499, comments - 13, trackbacks - 0, articles - 1

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

          1、引言

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

          • 1)作為完整的即時通訊產品進行應用:比如獨立的Web端IM產品;
          • 2)作為某個更大系統中的一部分進行應用:比如客服系統(相當于工單系統里嵌入IM技術啦)。

          對于第一種場景,為了更好的劃分功能邏輯,一個完整的產品通常都會調用來自于不同服務器提供的各種接口(比如各種服務端微服務接口),那么Web端跨域問題就無法回避了。

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

          所以,對于Web端即時通訊開發者來說,跨域問題是必須掌握的知識范疇。本文將為你講解跨域問題原理,以及理論聯系實際,用實踐代碼也為你演示解決跨域問題的幾種方法。

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

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

          學習交流:

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

          (本文同步發布于:http://www.52im.net/thread-2732-1-1.html

          2、什么是跨域問題

          前端調用的后端接口不屬于同一個域(域名或端口不同),就會產生跨域問題,也就是說你的應用訪問了該應用域名或端口之外的域名或端口。

          通俗的講,跨域問題是因為瀏覽器的同源策略規定某域下的客戶端在沒明確授權的情況下,不能讀寫另一個域的資源。而在實際開發中,前后端常常是相互分離的,并且前后端的項目部署也常常不在一個服務器內或者在一個服務器的不同端口下。前端想要獲取后端的數據,就必須發起請求,如果不做一些處理,就會受到瀏覽器同源策略的約束。后端可以收到請求并返回數據,但是前端無法收到數據。

          3、為什么會發生跨域問題

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

          • 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)跨站請求偽造)。

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

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

          5、跨域問題解決方法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 請求都是非簡單請求。

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

          6.1JSONP 是什么

          JSONP(JSON with Padding)是JSON的一種補充使用方式,不是官方協議,而是利用 Script 標簽請求資源可以跨域的特點,來解決跨域問題的,是一種變通的解決方案。(詳見《詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocke》一文中的第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 對象(但這也是為什么可以解決跨域問題的根本)。

          7、跨域問題解決方法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 服務器反向代理配置:

          8、本文配套的代碼下載

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

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

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

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

          關于Ajax短輪詢:
          找這方面的資料沒什么意義,除非忽悠客戶,否則請考慮其它3種方案即可。

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

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

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

          更多WEB即時通訊文章請見:
          http://www.52im.net/forum.php?mod=collection&action=view&ctid=15

          附錄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端即時通訊知識補課:一文搞懂跨域的所有問題!
          >> 更多同類文章 ……

          (本文同步發布于:http://www.52im.net/thread-2732-1-1.html



          作者:Jack Jiang (點擊作者姓名進入Github)
          出處:http://www.52im.net/space-uid-1.html
          交流:歡迎加入即時通訊開發交流群 215891622
          討論:http://www.52im.net/
          Jack Jiang同時是【原創Java Swing外觀工程BeautyEye】【輕量級移動端即時通訊框架MobileIMSDK】的作者,可前往下載交流。
          本博文 歡迎轉載,轉載請注明出處(也可前往 我的52im.net 找到我)。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 叶城县| 衡南县| 康马县| 玉门市| 宣化县| 三原县| 章丘市| 牟定县| 宝坻区| 宁化县| 丹棱县| 固阳县| 柳州市| 娄底市| 区。| 噶尔县| 白银市| 无极县| 浑源县| 北宁市| 黑龙江省| 长子县| 沙雅县| 禄丰县| 封丘县| 库伦旗| 城固县| 宿州市| 车致| 榆林市| 石狮市| 平昌县| 平遥县| 株洲市| 玉门市| 邯郸市| 介休市| 河津市| 榆社县| 柏乡县| 梅河口市|