首先說一下,跨域它不是一個問題,它是瀏覽器的一種安全策略,叫同源策略。拿前后端分離項目來說,前端調試或者部署的地址,與api的地址,必須擁有相同的協議、域名、端口號。否則,前端請求后端的接口會報錯。
如何實現跨域呢?
很多文章都有介紹,使用jsonp、cors服務端配置、代理等等,jsonp這種黑科技還是盡量不用吧,有很大的局限性,并且前后端改動都比較麻煩。
從項目實踐的角度來講,前后端分離項目應該這樣實現跨域:
開發階段,從我帶過的項目發現,很多時候前端會要求后端配置cors請求頭,然后后端無腦的設置Access-Control-Allow-Origin: *,上線的時候帶來很大的安全隱患。正確的姿勢應該是前端來解決,前端小白可能驚呆了,咋解決啊?前端框架vue、react都提供了代理功能,配置proxy選項就好了,具體怎么配置前端自己去查查,框架的官方文檔都有介紹。
正式上線,這個時候通常使用代理,前端請求接口地址一律使用當前域名下的/api/xxx/xxx。然后配置web服務器,對/api/開頭的請求進行代理,轉發到后端服務器或者后端部署端口。
前后端分離項目跨域問題是不可避免的。通常情況下前端由React、Vue等框架編寫,通過ajax請求服務端API,傳輸數據用json格式。
那么為什么有跨域的問題呢?解決跨域問題有哪些方式?搞清楚這兩個問題我們需要了解一下什么是同源策略。
瀏覽器的同源策略
同源策略(Same origin policy)是一種安全約定,是所有主流瀏覽器最核心也是最基本的安全功能之一。同源策略規定:不同域的客戶端腳本在沒有明確授權的情況下,不能請求對方的資源。同源指的是:域名、協議、端口均相同。
比如我們訪問一個網站
http://www.test.com/index.html,
那么這個頁面請求如下地址得情況是這樣的:另外,同源策略又分如下兩種情況:
-
DOM同源策略:禁止對不同源的頁面DOM進行操作,主要防止iframe的情況。比如iframe標簽里放一個支付寶付款的頁面,如果沒有同源策略,那么釣魚網站除了域名不同,其他的則可以和支付寶的網站一模一樣。
-
XMLHttpRequest同源策略:禁止使用XHR對象向不同源的服務器發起http請求。比如網站記錄了銀行的cookie,這個時候你訪問了惡意網站,黑客拿到你的cookie,再通過ajax請求之前的銀行網站,便可以輕易的拿到你的銀行信息。
所以,正是因為有了同源策略,大家的網絡環境才相對的安全一些。
跨域問題的解決辦法
了解了同源策略,就知道為什么會有跨域問題的產生了,都是為了安全。但是實際研發中,大家還是需要跨域去訪問資源。典型的應用場景就是前后端分離的項目了。那么我們如何去解決跨域問題呢?
CORS-跨域資源共享
CORS是一種W3C標準,定義了當產生跨域問題的時候,客戶端與服務端如何通信解決跨域問題。實際上就是前后端約定好定義一些自定義的http請求頭,讓客戶端發起請求的時候能夠讓服務端識別出來該請求是過還是不過。
瀏覽器將CORS請求分為簡單請求和非簡單請求:
簡單請求
簡單請求必須滿足以下兩個條件:
-
請求方式必須是HEAD、GET、POST三種方法之一。
-
Http請求頭必須只能是:Accept、Accept-Lanuage、Content-Lanuage、Last-Event-ID、Content-Type,其中Content-Type只限于三個值 Application/x-www-form-urlencoded、multipart/form-data、text/plain。
非簡單請求
不滿足簡單請求條件的就是非簡單請求。針對非簡單請求,瀏覽器會發起預檢請求。預檢請求的意思是當瀏覽器檢查到你的頁面含有跨域請求的時候,會發送一個OPTIONS請求給對應的服務器,以檢測服務器是否允許當前域名的跨域請求。如果服務端允許該域名請求,則返回204或200狀態碼,瀏覽器接收到允許請求時候再繼續發送對應的GET/POST/PUT/DELETE請求。同時服務器端也會告知瀏覽器預檢請求的緩存時長是多少,在這個時間范圍內,瀏覽器不會再次發起預檢請求。
原理基本上就是上面說的這些,實際業務中我們如何通過配置來解決跨域問題呢?基本上常見的就是三種方式:
Nginx配置
通常我們在nginx增加如下配置即可解決跨域問題:
用nginx這種方式是最舒服的,不需要客戶端和服務端多做其他工作,對代碼無入侵。
jsonp
因為script標簽是不受瀏覽器同源策略的影響,允許跨域請求資源(我們的每一個頁面都引用了大量第三方js文件)。所以可以利用動態創建script標簽,通過src屬性發起跨域請求,這就是jsonp的原理。但是jsonp只支持GET請求,所以并不是一種好的方式。
服務端代碼控制
可以在服務端增加對跨域請求的支持:
這種方式相當于全局過濾器,對所有請求都過濾一遍。
以上三種方式都可以一定程度上解決跨域問題,但是nginx配置和服務端控制不能同時存在,否則會報“Access-Control-Allow-Origin Not Allow Multiple value”的錯誤。個人比較推薦nginx配置的方式,一勞永逸,不需要每個web項目都去編寫跨域的代碼。
大家在工作中有沒有遇到過跨域問題呢?都是怎么解決的?歡迎評論區交流討論,共同學習~