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