由于 JavaScript 同源策略的存在使得一個源中加載來自其它源中資源的行為受到了限制。即會出現(xiàn)跨域請求禁止。
通俗一點說就是如果存在協(xié)議、域名、端口或者子域名不同服務(wù)端,或一者為IP地址,一者為域名地址(在跨域問題上,域僅僅是通過“ url的首部 ”來識別而不會去嘗試判斷相同的IP地址對應著兩個域或者兩個域是否同屬同一個IP),之中任意服務(wù)端旗下的客戶端發(fā)起請求其它服務(wù)端資源的訪問行動都是跨域的,而瀏覽器為了安全問題一般都限制了跨域訪問,也就是不允許跨域請求資源。
但很多時候我們卻又不得不去跨域請求資源,這個時候就需要我們想方法去繞過瀏覽器同源策略的限制了。
常見的跨域請求解決方法:
1.Jsonp 利用script標簽發(fā)起get請求不會出現(xiàn)跨域禁止的特點實現(xiàn)
2.window.name+iframe 借助中介屬性window.name實現(xiàn)
3.HTML5的 postMessage 主要側(cè)重于前端通訊,不同域下頁面之間的數(shù)據(jù)傳遞
4.Cors需要服務(wù)器設(shè)置header:Access-Control-Allow-Origin
5.Nginx反向代理 可以不需要目標服務(wù)器配合,不過需要Nginx中轉(zhuǎn)服務(wù)器,用于轉(zhuǎn)發(fā)請求(服務(wù)端之間的資源請求不會有跨域限制)
下面分別說一下常用幾種解決方案的具體實現(xiàn):
1 Jsonp
一般情況下由于同源策略我們不能夠通過XHR跨域去請求資源,但是我們的script標簽卻可以不受此限制成功的外鏈到來自于其他域下的資源。利用script標簽這個特點,我們可以成功的繞過同源策略。但這種方式存在局限性,我們只能夠發(fā)起get跨域請求。具體實現(xiàn):
客戶端
服務(wù)端
script標簽跨域發(fā)起get請求,同時傳遞在客戶端已經(jīng)注冊的全局函數(shù) process_fun 作為查詢參數(shù),服務(wù)端代碼提取查詢參數(shù),并傳入需要返回的數(shù)據(jù)。script標簽還有一個特點,就是會立即執(zhí)行調(diào)用所請求到的資源,所以這個時候服務(wù)端返回的 process_fun(data) 這一句代碼會被立即執(zhí)行,即把數(shù)據(jù) data 傳入我們事先定義好的函數(shù) process_fun 中執(zhí)行進一步的處理。
2 Cors
即跨域資源共享。實現(xiàn)cors通信的關(guān)鍵是服務(wù)器,只要服務(wù)器實現(xiàn)了cors接口,就可以跨源通信。不過不同于jsonp,cors對于IE8以下的瀏覽器是不支持的。
客戶端
服務(wù)端
跨域資源共享在我看來是最直接也最簡便的解決跨域問題的方式了,唯一的缺陷也就只是不能覆蓋支持所有的瀏覽器。客戶端不需要去做另外的改變,跟一般狀況下發(fā)送的異步請求一致就行,甚至客戶端根本不需要知道所請求的接口是跨域的。而服務(wù)端所需要做的也只是返回響應的同時設(shè)置Access-Control-Allow-Origin
響應頭部,意為“予許指定源(‘*’為任意源)發(fā)起跨域資源請求”。
3 window.name 和 postMessage
window.name 和 postMessage 主要都側(cè)重于純前端頁面之間的數(shù)據(jù)通訊,前者利用了 “ 在同一瀏覽器窗口載入的不同頁面( 無論它們是否不同域 ),共享同一個window.name,并且都對 window.name 有讀寫的權(quán)限 ” 的這一特性來實現(xiàn)頁面間的數(shù)據(jù)交換,后者則是Html5的API,不同域下的頁面在滿足一定關(guān)系的條件下可以通過此API跨域傳送數(shù)據(jù)。
4 Nginx反向代理
Nginx反向代理解決跨域問題則是利用了服務(wù)端之間的資源請求不會有跨域限制的特點實現(xiàn)的,具體來說就是我們前端發(fā)起的請求被Nginx攔截,再由Nginx代由轉(zhuǎn)發(fā)請求到資源服務(wù)器請求資源。
比如現(xiàn)在我們有兩個Nodejs服務(wù),分別是http://127.0.0.1:3000 和 http://127.0.0.1:5000,5000端口對應的服務(wù)端下的頁面需要發(fā)起請求3000端口所對應的服務(wù)端的資源,當然,在這種情況下如果不做任何的額外處理,請求會產(chǎn)生跨域。這個時候我們可以用Nginx來代理轉(zhuǎn)發(fā)我們的請求,前端不去直接對資源服務(wù)器發(fā)起請求,而是改為直接訪問Nginx服務(wù)器,看到這里你可能會問了,發(fā)起請求的前端頁面是屬于 http://127.0.0.1:5000 所在域下的,對Nginx服務(wù)發(fā)起請求難道不會和之前直接發(fā)起的請求一樣出現(xiàn)跨域嗎?所以這里需要明白的一點是,一開始我們可能是通過 http://127.0.0.1:5000/ 這樣的路徑訪問到我們的頁面的,但是如果我們使用Nginx作為反向代理,代理服務(wù)器監(jiān)聽8080端口,我們這時候再訪問該頁面就不再是訪問 http://127.0.0.1:5000/ , 而是 http://127.0.0.1:8080/ 了,在Nginx中我們再做這樣的配置:
就能成功訪問到首頁了,而這個時候首頁是在 8080 端口所對應的域下(即Nginx服務(wù))被渲染出來的,所以首頁這個時候便不再與 http://127.0.0.1:5000 同域, 而是與 Nginx 服務(wù)同域了,也就是說前端這時候?qū)?Nginx 服務(wù)發(fā)起請求就不會再是跨域。
訪問 Nginx 我們可以實現(xiàn)了,接下來要做的就是對請求進行代理轉(zhuǎn)發(fā)。
前端的 Ajax 請求是這樣的:
Nginx 需要對該請求進行攔截,所以可以做如下配置:
location配置的意思是對包含 “cross_origin” 請求攔截,并對請求路徑進行重寫,一開始請求路徑是 “/cross_origin/get_json?type=20170126” ,重寫后便成了 “/get_json?type=20170126”,$1代表(.*)中的內(nèi)容,而(.*)則代表 cross_origin 后面的全部字符,也就是我們會把 cross_origin 部分去掉,但是保留 cross_origin 之后的所有字符,當然這一步并非是絕對必要的。接下來我們再把請求代理到 3000 端口所對應的資源服務(wù),所以請求路徑從一開始的 “127.0.0.1:8080/cross_origin/get_json?type=20170126” 經(jīng)過代理服務(wù)器Nginx之后變成了 “127.0.0.1:3000/get_json?type=20170126”。在 “127.0.0.1:3000” 下存在對應的接口:
現(xiàn)在在前端觸發(fā)點擊事件發(fā)起請求:
得到了響應,至此我們成功發(fā)起了跨域請求。
完。