前端跨域的那些總結(jié)
做項(xiàng)目期間一直有遇到關(guān)于跨域方面的問題,之前由于沒有上過生產(chǎn)環(huán)境,對(duì)于這方面的問題還不夠全面致讥,只是在前后端分離的方面遇到過,還是本地環(huán)境器赞,主要是基于JSONP解決的垢袱;在最近的Vue項(xiàng)目中遇到的本地開發(fā)環(huán)境的跨域解決臨時(shí)使用了dev中的index.js配置文件下內(nèi)置的proxyTable屬性配置解決跨域,測(cè)試環(huán)境行的通港柜,但是生產(chǎn)環(huán)境則不行请契,會(huì)踩到這,還是對(duì)跨域不夠清楚夏醉,最后重新復(fù)習(xí)了關(guān)于跨域方面的知識(shí)爽锥,對(duì)于線上環(huán)境配置了nginx反向代理后解決了跨域問題;下面畔柔,總結(jié)下對(duì)于跨域的認(rèn)識(shí)氯夷。
- 為什么會(huì)有跨域問題的出現(xiàn)?
在我們的瀏覽器中有一條策略叫做“同源策略”靶擦,什么叫同源策略呢腮考?也就是當(dāng)我們前后端數(shù)據(jù)交互的時(shí)候,如果兩個(gè)頁面所處的端口或者子域名以及通信協(xié)議中的任何一個(gè)不同玄捕,那么當(dāng)你需要在違反同源策略的情況下在兩個(gè)頁面間通信時(shí)秸仙,就會(huì)出現(xiàn)跨域錯(cuò)誤,因?yàn)闉g覽器出于安全考慮桩盲,限制了此類訪問操作寂纪;但是日常開發(fā)操作中對(duì)于跨域操作的使用非常頻繁,所以如何解決此類限制赌结,完成不同頁面間的數(shù)據(jù)通信捞蛋,就是我們學(xué)習(xí)跨域操作及其工作原理的原因。
-
前端有哪些處理方式可以解決跨域問題柬姚?
1拟杉、document.domain1.場(chǎng)景:瀏覽器的同源策略有一些限制,首先量承,不能用ajax方法去請(qǐng)求不同源的資源搬设;并且穴店,瀏覽器中不同域的框架之間是不能進(jìn)行數(shù)據(jù)通信的,假如從‘https://www.baidu.com/request.html’中請(qǐng)求‘https://baidu.com/response.html’中的數(shù)據(jù)拿穴,在兩個(gè)不同的源中有一個(gè)iframe泣洞,但是由于是不同域,我們就沒法通過JS來訪問iframe中的數(shù)據(jù)和方法默色。
解決辦法:我們可以將兩個(gè)不同源文件中的document.domain設(shè)成相同的域名球凰,但這里,需要提醒的一點(diǎn)是腿宰,我們?cè)谠O(shè)置document.domain時(shí)候呕诉,只能將其設(shè)置成自身或更高一級(jí)的父域,并且二者的主域必須相同吃度。
``` javascript
<iframe src="https://www.baidu.com/request.html">
<script>
document.domain = " baidu.com " //這里是將A中的document.domain設(shè)置成主域
</script>
<iframe src="https://baidu.com/response.html">
<script>
document.domain = "baidu.com" //B頁面的document.domain設(shè)置相同的domain即可
</script>
// 但是甩挫,此類解決辦法只適用于不同子域的框架間的交互。
```
2椿每、location.hash
場(chǎng)景:對(duì)于頁面中有iframe的頁面中捶闸,父窗口可以對(duì)iframe的url進(jìn)行讀寫。而在URL上有一部分#加上后面的字符可以用來進(jìn)行錨點(diǎn)定位拖刃,這部分就是這里會(huì)提到的hash。利用修改URL的hash部分可以進(jìn)行雙向通信贪绘,從而達(dá)到跨域的目的兑牡,每個(gè)window通過改變其他window的location來發(fā)送消息,其他窗口通過監(jiān)聽URL的變化來接受消息税灌,這個(gè)方式的通信會(huì)造成一些不必要的瀏覽器歷史記錄均函,而且有些瀏覽器不支持onhashchange事件,需要通過輪詢操作來獲取URL的改變菱涤,最后苞也,這樣做也存在一定問題,就是不僅數(shù)據(jù)會(huì)直接暴露在url中粘秆,數(shù)據(jù)容量和類型都有限如迟。
示例:假如當(dāng)前父頁面' baidu.com/request.html ',其中嵌入的頁面是’ xupt.edu.cn/response.html ‘攻走,要實(shí)現(xiàn)此兩個(gè)頁面間的通信可以通過以下方法殷勘。
> 1.request.html傳送數(shù)據(jù)到 response.html , request.html下修改為iframe的src為 'xupt.edu.cn/response.html'
>2.response.html監(jiān)聽到相應(yīng)數(shù)據(jù)發(fā)生變化昔搂,觸發(fā)對(duì)應(yīng)操作玲销。
>3.response.html傳送數(shù)據(jù)到 request.html 中,但是由于兩個(gè)頁面不在同源下摘符,會(huì)受到限制贤斜,所以要借助父窗口域名下的一個(gè)代理iframe策吠。
>4.request.html下創(chuàng)建一個(gè)隱藏的iframe, 此Iframe的src是baidu.com域下的瘩绒,并掛上要傳送的hash數(shù)據(jù)猴抹;如 src = "baidu.com/proxy.html"
>5.proxy.html監(jiān)聽到URL變化,修改a.html變化的URL,由于二者是同源草讶,所以在proxy.html中可修改a.html的url hash
>6.request.html監(jiān)聽到url變化洽糟,觸發(fā)相應(yīng)操作。
// request.html 簡(jiǎn)單處理代碼
try{
parent.location.hash = 'data';
}catch(e){
// ie Chrome存在安全機(jī)制堕战,所以無法修改 parent.location.hash
var _proxy = document.createElement('iframe');
_proxy.style.display = 'none';
_proxy.src = 'baidu.com/proxy.html';
}
// proxy.html關(guān)鍵代碼處理
// 由于proxy.html和baidu.com/request.html存在與同域坤溃,所以可以正常改變其location.hash的值。
parent.parent.location.hash = self.location.hash.substring(1);
3嘱丢、Html5的 postMessage( )
場(chǎng)景:該屬性各大主流瀏覽器均支持薪介,主要是包括接收信息的方法和發(fā)送信息的postMessage方法,比如A頁面通過內(nèi)嵌Iframe越驻,獲取一個(gè)B頁面汁政,就可以通過此方法實(shí)現(xiàn)A頁面和B頁面的通信。
// A頁面通過 postMessage() 發(fā)送消息
window.onload = function (){
var _iframe = document.getElementById('ifr');
var targetOrigin = "https://www.google.com";
_iframe.contentWindow.postMessage('hello world!', targetOrigin);
}
// 在B頁面通過監(jiān)聽 message 事件并接收消息
var onmessage = function (event){
var data = event.data;//消息
var origin = event.origin;//消息來源地址
var source = event.source;//源Window對(duì)象
if(origin=="https://www.baidu.com"){
console.log(data);//hello world!
}
};
if (typeof window.addEventListener != 'undefined') {
window.addEventListener('message', onmessage, false);
} else if (typeof window.attachEvent != 'undefined') {
//IE瀏覽器需要做特殊處理
window.attachEvent('onmessage', onmessage);
}
5缀旁、通過JSONP跨域
場(chǎng)景:通過script標(biāo)簽引入的JS不回受到瀏覽器同源策略的限制记劈,所以我們可以通過生成的script標(biāo)簽引入一個(gè)JS或者其他后綴形式的文件,但是這些文件返回的是一個(gè)JS函數(shù)的調(diào)用并巍。
> 如果作為一個(gè)JS文件引入目木,那么接口或請(qǐng)求地址返回的必須是一個(gè)可以執(zhí)行的JS文件,這里需要在編寫的時(shí)候和后端約定好規(guī)范懊渡。
- 雖然此方法兼容性好刽射,不用XMLHttpRequest或ActiveX的支持;并且在請(qǐng)求完畢后可以通過調(diào)用callback的方式回傳結(jié)果剃执;但是只支持GET請(qǐng)求誓禁,不能解決不用域的兩個(gè)頁面之間如何進(jìn)行Javascript調(diào)用的問題。
6肾档、通過CROS跨域
場(chǎng)景:CROS全稱是跨域資源共享摹恰,定義了必須在訪問跨域資源時(shí)瀏覽器與服務(wù)器應(yīng)該如何溝通,CROS背后的基本思想就是使用自定義的HTTP頭部讓瀏覽器與服務(wù)器進(jìn)行數(shù)據(jù)通信怒见,主流瀏覽器均支持該功能戒祠,但是IE瀏覽器版本不能低于IE10。并且速种,使用此方法的關(guān)鍵在于服務(wù)器的配置處理姜盈,只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以實(shí)現(xiàn)跨域通信配阵。
在服務(wù)端里面馏颂,對(duì)于CROS的支持示血,主要是通過設(shè)置響應(yīng)頭Access-Control-Allow-Origin來進(jìn)行,如果瀏覽器監(jiān)測(cè)到相應(yīng)的設(shè)置救拉,就可以允許跨域通信請(qǐng)求难审。
上邊提到的JSONP實(shí)現(xiàn)跨域,有很大的局限性亿絮,只支持GET請(qǐng)求告喊,但是在CROS實(shí)現(xiàn)的跨域請(qǐng)求訪問中,支持所有的數(shù)據(jù)請(qǐng)求方式派昧,并且有更好的錯(cuò)誤處理方式黔姜,能夠接收簡(jiǎn)單的XMLHttpRequest發(fā)起的請(qǐng)求。
7蒂萎、設(shè)置Window.name屬性
場(chǎng)景: 每個(gè)window對(duì)象有個(gè)name屬性秆吵,該屬性有個(gè)特征:在一個(gè)創(chuàng)口的生命周期之內(nèi),窗口載入的所有頁面都是共享一個(gè)window.name的五慈,每個(gè)頁面對(duì)window.name都有讀寫的權(quán)限纳寂,window.name是持久存在一個(gè)窗口載入過的所有頁面中的,并不會(huì)因?yàn)樾马撁娴妮d入而進(jìn)行重置泻拦;并且毙芜,由于安全原因限制,瀏覽器始終都會(huì)保持window,name是 string 類型争拐。
這種方法與document.domain相比腋粥,放寬了域名后綴要相同的限制,可以從任意頁面獲取string類型的數(shù)據(jù)陆错。
8、配置反向代理服務(wù)器
場(chǎng)景:基本解決思路就是將其服務(wù)所在的服務(wù)器配置成所需的跨域資源訪問的反向代理服務(wù)器金赦,在nginx服務(wù)器配置上音瓷,將當(dāng)前接收到需要發(fā)送的各類請(qǐng)求鏈接,按照實(shí)際需要夹抗,配置好轉(zhuǎn)發(fā)規(guī)則绳慎,通過服務(wù)器處理相應(yīng)的請(qǐng)求鏈接,對(duì)于瀏覽器來說漠烧,這些請(qǐng)求是發(fā)送到本機(jī)服務(wù)器的杏愤,是同源的,而實(shí)際請(qǐng)求是又服務(wù)器進(jìn)行轉(zhuǎn)發(fā)已脓,這樣就不會(huì)觸及瀏覽器的同源策略限制珊楼,就能實(shí)現(xiàn)正常的跨域通信請(qǐng)求等操作。