瀏覽器跨域請求與同源策略

2020-07-16 10:04:48
跨域和同源是前端開發領域中很基本的概念,但是很多沒做過前端開發工作的程式設計師不是很了解它們的使用場景,產品經理對它們的了解可能更少。

首先,我們介紹 iframe。iframe 是 HTML 中的一個標籤,它可以隨意指定一個 URL 地址,比如 www.qq.com/index.html,它的程式碼如下:
<body>
    <iframe id="ifr" src="www.qq.com"></iframe>
</body>
iframe 裡面的 src 欄位為 www.qq.com。開啟這個網頁後,會看到騰訊網的整個頁面嵌入了這個 index.html 網頁。iframe 的意義非常簡單,就是將一個 URL 地址嵌入當前頁面並展示出來。

例如,你給遠方的親人寫了一封家書,當把郵票貼在信封右上角的時候,可以把信封想象為頁面,把郵票想象為一個 iframe 標籤,它描寫了很多內容(有山、有水、有人家)。

信封是在超市買的,郵票是在郵局買的,它倆的生產廠家、品牌、材質毫不相干,但組合在一起可以發揮作用。信封屬於“超市”這個域,郵票屬於“郵局”這個域。

如何實現這樣一種一個網站有 3 個展示頁面,3 個頁面共用同一個評論區的需求呢?這個評論區可以封裝為一個 URL,並將 iframe 嵌入每個頁面,全域性複用一套評論區的程式碼,既節省人力,又便於維護邏輯,只要一個人就可以實現。

這就是 iframe 大致的用途:嵌入另一個頁面,兩個頁面的功能可以解耦合,不依賴對方而存在。

接下來,我們介紹“跨域”。還是以上述程式碼段為例,通過 iframe 嵌入的方式,開發者可以設計出一個與騰訊網外觀一模一樣的網頁。同時,開發者動了壞心思,把頁面中騰訊網的廣告位置變為自己的廣告,想靠這些流量來掙錢,於是將程式碼改造為:
<html lang="en">
    <script>
        1.得到id為ifr的iframe的文件物件
        2.得到ifr裡面的www.qq.com的廣告標籤
        3.替換www.qq.com廣告標籤裡的內容為我聯絡的廣告商內容
    </script>
    <body>
        <iframe id="ifr" src="www.qq.com"></iframe>
    </body>
</html>
在這個 <script> 標籤中,開發者寫了 3 句 JS 程式碼來描述整個流程(為了省去偵錯的時間,這裡用中文偽碼代替),這樣做的結果是:這個功能會被瀏覽器拒絕,提示 "Permission Denied”,也就是當前“跨域”操作了,開發者無法篡改騰訊網的頁面。

最後,我們介紹同源。跨域被拒絕,其實是瀏覽器底層被稱為“同源策略”的安全機制起了作用,什麼是同源呢?只要兩個頁面的協定、主機名、埠一樣,就是同源的,否則就是非同源的。

同源要同時滿足 3 個特徵,例如 http://www.a.com/index.html 和 http://www.b.com/index.html 不同源,因為主機名不同,一個為 a.com,另一個為 b.com。

http://www.a.com/index.html 和 https://www.a.com/index.html 不同源,因為協定不同,一個為 HTTP,另一個為 HTTPS。https://www.a.com:80/index.html 和 https://www.a.com:81/index.html 不同源,因為埠號不同,一個為 80,另一個為81。

同源就是同域,“跨域”也可以說成“跨源”。不同源,就不能修改另一個頁面,更不能獲取與另一個頁面相關的內容。只有同源的頁面才可以相互存取。

瀏覽器提供了原生的同源機制來保證不同域下的網站互相隔離,正是這種機制的存在,保證了 Web 生態下各個網站不亂套。

這也引出了一個問題:瀏覽器天生是拒絕非同源的網頁溝通的,但是溝通需求無處不在。例如上面的評論區的例子,如果評論區頁面是用 iframe 實現的,當有一個新評論時,主頁面要展示評論數 +1,這時就產生了溝通的需求。

同源策略基本上保證了域之間的隔離,如果要溝通,是要用一些附加的方法來實現的,例如後台的配合、兩個網站之間的配合。

合理的跨域溝通的方法有以下幾種:
  • JSONP
  • iframe document.domain
  • iframe location.hash
  • HTML 5 PostMessage

第 4 種是較新的 HTML 5 規範,規定了跨域問題的解決辦法,並且是非同步的,大部分瀏覽器已經支援。前面幾種有點走偏門的感覺,而且有的方法有些局限性,這裡重點推薦第 4 種,大家有興趣可以深入了解。