01.13 詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

這篇文章我們聊一聊CORS跨域,它的全稱是"跨域資源共享"(Cross-origin resource sharing)。

在之前的文章中我們已經詳細介紹瞭如何使用JSONP進行接口跨域請求,如果不瞭解的可以參考作者之前的文章 ,相信一定難不倒聰明的你。

那麼CORS跨域方案和jsonp跨域有何不同呢?讀完這篇文章你肯定能找到答案!

跨域案例

頁面地址:http://client.cors.com:8000/greeter.html,代碼如下:

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖1

服務器接口地址:http://server.cors.com:3000/data,服務器代碼如下:

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖2

很明顯,當頁面在請求服務器接口時會發生跨域現象,如下:

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖3

我們去瀏覽器Network中看一下請求信息,

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖4

如圖4所示,響應為200,response Headers信息也很正常,這說明在跨域的情況下請求依然可以到達服務器,並且服務器能夠正常響應,但是由於瀏覽器的同源策略並沒有把返回的數據給到頁面。

那麼如何讓頁面在跨域的情況下獲取到數據呢?我們回看圖3,似乎在說少了一個Access-Control-Allow-Origin頭,那麼我們在服務器代碼中加一下。

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖5

現在刷新頁面,服務器返回的數據就能正常打印出來了。

'Access-Control-Allow-Origin': '*'表示接受任意域名的請求

攜帶憑證

在跨域的情況,服務器有時依然需要鑑權。通常服務器鑑權都是從cookie中獲取信息來判斷客戶端的身份,那麼跨域的情況下請求還能傳遞cookie嗎?當然能,但是cookie會遵守同源策略!

1)服務器設置cookie

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖6

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖7

如果需要服務器設置cookie,必須設置Access-Control-Allow-Credentials: true,否則會出現如下錯誤。

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖8

頁面中的xhr對象也必須設置屬性withCredentials=true。

此時刷新頁面,在頁面控制檯中通過document.cookie查看server=123,你會發現server端設置的cookie並不存在。上面已經說了cookie會遵循同源策略,服務器的域名是server.cors.com,所以服務器設置的cookie應該是在這個域名下,並不會在頁面的域名(client.cors.com)下,那如何驗證服務器設置cookie成功呢?

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖9

  • 先打開接口頁面,這個頁面是同源的;
  • 回到請求頁面,刷新;
  • 再回到接口頁面,在控制檯通過document.cookie就可以拿到服務器設置的cookie。

2)頁面設置cookie

如果主域名相同,比如現在的例子,主域名都是cors.com,那麼就可以把cookie設置在這個主域名下。

<code>document.cookie="client=1;domain=cors.com;"/<code>

這樣服務器就能獲取到頁面設置的cookie。

如果連主域名都不一樣,那就不要妄想在頁面上設置cookie讓服務器獲取到。這種情況下,服務器該如何鑑權呢?

第一種方式是讓後端把跨域的接口代理成同域的,這樣我們的後端可以拿到cookie,在他那把cookie轉發給跨域服務。

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖10

第二種方式是頁面發送請求時在header中附加一個token,用於鑑權,

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖11

當刷新頁面時,頁面控制檯又報錯了。

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖12

提示設置Access-Control-Allow-Headers,那我們就設置一下。

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖13

再刷新頁面,請求正常了,服務端也能拿到token的值了。

簡單請求與非簡單請求

圖13中我們拿到了token的值,此時我們再去瞧瞧瀏覽器中的Network,會發現頁面發送了兩個請求,第一個請求的method是OPTIONS,這是怎麼回事呢?

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖14

原來cors跨域也分簡單請求和非簡單請求。

簡單請求條件如下:

  • 請求方法是必須是HEAD/GET/POST三種方法之一;
  • HTTP的頭信息不超出這幾種字段:Accept/Accept-Language/Content-Language/Content-Language/Last-Event-ID/Content-Type,Content-Type只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain。

圖11中我們設置了token請求頭,顯然不滿足以上條件,所以是非簡單請求。非簡單請求的CORS請求會在正式通信之前增加一次HTTP查詢請求,稱為預檢請求(preflight)。瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。預檢請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。

我們現在嘗試發送一次PUT請求,看看會有什麼現象?

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖15

不出所料,瀏覽器再次報錯!

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖16

提示我們設置Access-Control-Allow-Methods,那就只能設置了!

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖17

再次刷新頁面,現在請求正常了!我們回頭看一下預檢請求,

詳解CORS跨域內部機制,幫助前端克服瀏覽器同源策略

圖18

不得不說,瀏覽器在訪問跨域接口時真的是非常的小心,當然這一切都是為了安全考慮。即使這樣,現在網絡中也不乏XSS、CSRF等攻擊。

總結

17年夏天作者去頭條面試,當時筆試有這麼一道題“如果瀏覽器請求跨域,那麼這個請求還能到達服務器嗎?如果能,服務器會返回什麼?”。如果你讀完本文,並且能充分理解,我相信這道題肯定不在話下。跨域在業務中經常遇到,大部分後端同學並不知道什麼叫跨域,更不清楚該如何解決。作為前端的你,這時候就可以大顯身手了!

喜歡我的文章就關注我吧,有問題可以發表評論,我們一起學習,共同成長!


分享到:


相關文章: