這些年我們處理過的跨域


同源策略

在說跨域之前,我們需要先了解下 同源策略。它是一個規范(Netscape 1995年提出),並沒有指定具體的使用范圍和實現方式。

為了保證使用者信息的安全,防止惡意網站篡改用戶數據,一些常見的Web技術都默認采用了同源策略(如Silverlight, Flash, XMLHttpRequest, Dom等)。

那如何判斷同源呢?

  1. 相同的協議
  2. 相同的域名
  3. 相同的的端口號

我們用一個表格來展示同源的判斷:

URL1 URL2 是否同源 分析
http://www.a.com https://www.a.com 不同源 協議不一致
http://www.a.com http://www.b.com 不同源 域名不一致
http://www.a.com http://www.a.com:8080 不同源 端口不一致
http://www.a.com http://test.a.com 不同源 域名不一致
http://www.a.com http://www.a.com/test 同源 判斷同源和path無關
http://www.a.com http://www.a.com:80 同源 不帶端口訪問時,默認是80

如果不是同源會有哪些使用限制呢?

  1. Cookie,WebStorage(LocalStorage, SessionStorage),Cache(Application Cache, CacheStorage),Web DB(WebSql IndexDB)等都無法共享

  2. 無法彼此操作各自的DOM(Iframe)

  3. 無法發送Ajax請求

  4. 其他

注意:如果兩個站點,具有相同的一級域名(如 www.a.com, test.a.com,一級域名都是a.com),那么可以通過各自設置document.domain='a.com' 來共享Cookie。

注意2:如果是iframe非同源,雖然不能操作dom,但是能操作location.href。

什么是跨域?

通過以上內容,我們了解到了什么是同源策略,以及怎么判斷同源。那么與之相反,如果不滿足同源,則就是跨域。

在瀏覽器上,如果訪問跨域資源,將會有諸多限制(為了安全),參考上面的同源限制。

注意:跨域限制是瀏覽器的機制,如果直接在服務端請求,是不會觸發跨域限制的。

那些我們遇到的跨域

1、圖片跨域

對於圖片來說,大部分場景是不需要處理跨域限制的,因為一般來說,圖片沒有跨域限制。

在也有例外,如果在 Canvas 中操作跨域的圖片,那么就會觸發跨域限制。解決辦法是在返回圖片的時候,添加 Access-Control-Allow-Origin: orign | '*' 來允許跨域。

2、Iframe跨域

這個也不太常見,如果網站本身和iframe嵌入的站點都是我們自己可以控制的,那么應直接使用 postMessage 來通信。如果瀏覽器較舊,不支持 postMessage ,可以考慮通過window.name來傳遞數據。

window.name 傳遞數據原理

首先在iframe訪問跨域的站點,這個站點,將數據寫入到window.name中。

然后主站點,修改iframe的location.href='about:blank' 或其他不跨域的站點。

最后通過window.name獲取數據

這是因為同一個iframe的window.name是相互共享的。在現代瀏覽器中,該方式可能會失效,此時請使用 postMessage。

3、字體跨域

跨域使用字體文件,也會觸發攔截。這個的解決辦法和圖片跨域一致,后端設置CORS頭部即可。

4、Ajax跨域

這是我們經常會遇到的跨域問題,由於現在流行的開發模式,很多時候我們都需要處理這類型的跨域。

如何判斷Ajax跨域

當我們在訪問一個Ajax請求,控制台出現如下錯誤時,我們基本可以判斷,是被跨域攔截了:

XMLHttpRequest cannot load xxxxx. No 'Access-Control-Allow-Origin' header is present on the requested resource. 

很多時候,我們的API和Web並不在一個站點上(多個域名),而我們又必須要跨域訪問。這個時候我們就需要想辦法實現跨域資源訪問

以下,我們就來看看如何實現跨域資源訪問:

CORS(跨域資源訪問)- 標准做法,強烈推薦

開發模式的演進,導致我們很多的應用都是跨域訪問。這個時候CORS規范也就應運而生了。使用它,我們可以直接對跨域資源進行訪問,了解更多,請參考CORS詳解

該方式的核心是通過和后端API協商,看是否允許跨域訪問。對於滿足某些條件的請求,會先發送一個預請求。簡單請求,也需要服務器允許跨域訪問。

最關鍵的的幾個響應頭如下:

  1. Access-Control-Allow-Origin: origin | '*' 允許某個指定的域訪問,*表示不限制域。
  2. Access-Control-Allow-Methods: 'GET,POST,PUT,DELETE' 允許哪些類型的請求
  3. Access-Control-Allow-Headers: 'x-token' 允許的自定義Header。

注意:該方式由服務端設置,前端無需設置,也無法設置。只要服務端處理好了,前端不需要做任何處理即可使用。

反向代理(將跨域代理為同域,繞過)

既然跨域有限制,那么我們可以考慮將跨域變成同域,這樣不就沒有限制了么?

Nginx 為例,我們只需要將特定路徑的請求轉發給真正的后端API即可:

server {
    listen 8101;
    root /dist;
    index index.html;

    location ~* \.(eot|ttf|woff|woff2)$ {
        add_header x-server $server_addr;
        add_header Access-Control-Allow-Origin '*';
    }
    
    location ^~ /api/v1 {
        proxy_pass http://apis.xxx.com/api/v1;
    }
}

注意:該方式需要在部署的時候做處理,前端需要修改請求api的地址為同域。

服務端轉發(通過不跨域的請求跨域API,繞過)

該方式,通過請求不跨域的api,然后在api中再呼叫真實的跨域api,由於是服務端請求,所以也就避開了跨域問題。整體看來,這種方式有點多此一舉,不過如果把這個轉發由統一的程序進行處理,還是挺不錯的。

注意:該方式在后端API中處理,前端需要修改請求api地址為同域。

JSONP(利用script無跨域限制,繞過)

該方式利用Script請求資源不會觸發跨域限制這個特點來實現。JSON原理,請參考JSONP詳解

注意:該方式需要前后端搭配,后端需要支持JSONP請求,前端需要采用JSONP的方式去請求數據。

注意2:該方式由於實現原理限制,只能處理GET請求。


綜上,遇到跨域請求,就先去找后端啊。前端真的獨自搞不定啊。

總結

跨域是項目開發中,非常常見的問題。就算是前端開發,也一定要理解跨域,了解跨域的處理方案。以便於能夠真正的處理好開發任務(或許,這樣和后端交(Si)流(Bi)也更有底氣)。


免責聲明!

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



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