關於 websocket 跨域的一個奇怪問題…


作者:fredalxin

地址:https://fredal.xin/websocket-cors-problem

最近在建設websocket長連接網關,過程中遇到一件比較奇怪的事情,做下簡單的記錄。

需求十分的簡單,websocket網關在做權限校驗的時候期望復用現有登錄邏輯的jwt-token。如下圖所示,sso與websocket網關屬於不同的二級域名,登錄的jwt-token cookie的domain設置為*.xx.com。

所以我們的期望是瀏覽器與websocket網關進行handshark請求時可以帶上jwt-token cookie。

結果自然是不行的,服務端並沒有收到來自*.xx.com的cookie。於是開始考慮可能和跨域行為有關系。

CORS

CORS 是一種用於解決跨域的w3c標准,全稱為"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出 XMLHttpRequest 請求,從而克服了 AJAX 只能同源使用的限制。CORS 基於 http 協議關於跨域方面的規定,使用時,客戶端瀏覽器直接異步請求被調用端服務端,在響應頭增加響應的字段,告訴瀏覽器后台允許跨域。

概括的說,CORS就是服務端對跨域權限的控制,由一組標准的header來控制客戶端的跨域行為,不同瀏覽器對於CORS的實現均有不同。

常用的CORS header主要有:

  • Access-Control-Allow-Origin : 指示請求的資源能共享給哪些域,可以是具體的域名或者*表示所有域。
  • Access-Control-Allow-Credentials : 指示當請求的憑證標記為 true 時,是否響應該請求。
  • Access-Control-Allow-Headers : 用在對預請求的響應中,指示實際的請求中可以使用哪些 HTTP 頭。
  • Access-Control-Allow-Methods: 指定對預請求的響應中,哪些 HTTP 方法允許訪問請求的資源。

CORS處理請求的流程如下:

  1. 判斷當前請求是否簡單請求。
  2. 如果不是簡單請求,則會使用OPTIONS方法先發起一個預檢請求(PreFlight),預檢請求通過返回的response里設置了對應的header並匹配上了才會進行下一步具體的請求。
  3. 預檢請求后會發起實際請求,但會根據返回的response header來決定請求行為,例如根據服務端設置的Access-Control-Allow-Credentials值來決定請求是否攜帶當前域的cookie。

這里涉及到的簡單請求和非簡單請求的概念,那么簡單請求和非簡單請求有什么區別呢?若請求滿足所有下述條件,則該請求可視為簡單請求:

  1. 使用了下列 HTTP 方法:GET、HEAD、POST。
  2. 只用了以下header:Accept、Accept-Language、Content-Language、Content-Type(有額外限制)、DPR、Downlink、Save-Data、Viewport-Width、Width。
  3. 請求中的任意XMLHttpRequestUpload 對象均沒有注冊任何事件監聽器;XMLHttpRequestUpload 對象可以使用 XMLHttpRequest.upload 屬性訪問。
  4. 請求中沒有使用 ReadableStream 對象。

經過一番簡單的科普,回到我們的問題上來。瀏覽器對websocket的handshark請求會不會應用同源策略呢。我們先不回答,先來看看如果CORS應用在websocket上會是什么樣的。

首先一個websocket的握手連接報文大概如下:

GET / HTTP/1.1

Upgrade: websocket

Connection: Upgrade

Host: ws.xx.com

Origin: http://www.xx.com

Sec-WebSocket-Key: sB9cRrP/a9NdMgdcy2VJFX==

Sec-WebSocket-Version: 11

它和普通HTTP請求的區別是多了兩行header

Upgrade: websocket

Connection: Upgrade

顯然它們不屬於CORS安全的header集合,自然瀏覽器會認為這不是一個"簡單請求"。那么它會按照發起"預檢請求",隨后根據返回的response header來判斷下一步行為。此處我們希望能帶上當前域的cookie,那么按照CORS標准,我們需要在服務端做一些配置,讓其支持CORS並帶上Access-Control-Allow-Credentials為true的response header。

我們使用的是Netty來構建websocket網關,Netty支持CORS很簡單:

CorsConfig corsConfig = CorsConfigBuilder.forAnyOrigin().allowNullOrigin().allowCredentials().build();

pipeline.addLast(new CorsHandler(corsConfig));

結果是什么呢?我們的websocket服務端正確拿到了*.xx.com的cookie,並完成了后續鑒權工作。

websocket需要CORS么?

所以真相是什么呢?websocket也需要CORS支持來避免跨域問題么?

google任何websocket與跨域相關的問題都會告訴你,websocket本身就是支持跨域的,websocket本身沒有同源策略!也就是說,在第一幅圖中,我們應該不作任何事就可以把xx.com的cookie帶到ws.xx.com的websocket網關上去,這似乎和我們實際情況不符。

我們使用的是chrome,后來突發奇想試了下firefox與safari,結論是這兩者不用配置任何CORS相關屬性就可以把cookie帶上。難道這是chrome的一個bug?翻了翻網絡,找到了一個似乎可以應征的bug report:https://bugs.chromium.org/p/chromium/issues/detail?id=947413

近期熱文推薦:

1.600+ 道 Java面試題及答案整理(2021最新版)

2.終於靠開源項目弄到 IntelliJ IDEA 激活碼了,真香!

3.阿里 Mock 工具正式開源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式發布,全新顛覆性版本!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM