摘要:由于瀏覽器的同源策略保護(hù)機(jī)制,瀏覽器不能執(zhí)行來自其他來源的腳本。然后想要得到所獲取到的數(shù)據(jù),也就是想要得到的的值,還必須把這個(gè)的設(shè)成跟頁面同一個(gè)域才行,不然根據(jù)前面講的同源策略,是不能訪問到里的屬性的。
由于瀏覽器的同源策略保護(hù)機(jī)制,瀏覽器不能執(zhí)行來自其他來源的腳本。通過 js 在不同的域之間進(jìn)行數(shù)據(jù)傳輸或通信,比如用 ajax 向一個(gè)不同的域請(qǐng)求數(shù)據(jù),或者通過 js 獲取頁面中不同域的框架中(iframe)的數(shù)據(jù)的操作就叫跨域。
所謂同源,就是指協(xié)議、域名(IP)、端口三者都相同。只要有其中一者不相同,都是跨域,無法進(jìn)行數(shù)據(jù)的交流。例如:
注意的是,localhost 與 本機(jī)的 IP 地址也屬于跨域。
瀏覽器執(zhí)行 javascript 腳本時(shí),會(huì)檢查這個(gè)腳本屬于哪個(gè)頁面,如果不是同源頁面,就不會(huì)被執(zhí)行。
解決方法主要有以下幾種:
方法一:設(shè)置 webpack 的服務(wù)器代理1、在工程化的前端項(xiàng)目中,在本機(jī)進(jìn)行開發(fā)時(shí),后臺(tái)的接口可能會(huì)出現(xiàn)跨域,可以設(shè)置 webpack 的服務(wù)器代理,將本機(jī)的請(qǐng)求轉(zhuǎn)發(fā)到與接口同源的地址。
首先安裝插件:webpack-dev-server,然后再 config 文件夾里找到 index.js 文件,在 dev 對(duì)象下的 proxyTable 字段添加對(duì)應(yīng)的需要替換的地址。
module.exports = { ... devServer: { //本地開發(fā)服務(wù)器設(shè)置 ... port: 8080, //當(dāng)前的端口號(hào) proxyTable: { //服務(wù)器代理設(shè)置 "/v3/admin": { //需要代理的接口形式 target: "http://localhost:8700", //將當(dāng)前端口號(hào)代理到8700 changeOrigin: true, secure: false } } } }
以上設(shè)置就是將所有包含 "/v3/admin" 字段的接口,從原來的 localhost: 8080 代理到 localhost: 8700 ,變成 "http://localhost: 8700/v3/admin" 從而獲取到 8700 端口的內(nèi)容。
CORS是一個(gè)W3C標(biāo)準(zhǔn),全稱是"跨域資源共享"(Cross-origin resource sharing)。
它允許瀏覽器向跨源服務(wù)器,發(fā)出 XMLHttpRequest 請(qǐng)求,從而克服了 AJAX 只能同源使用的限制。
CORS 標(biāo)準(zhǔn)新增了一組 HTTP 首部字段,允許服務(wù)器聲明哪些源站有權(quán)限訪問哪些資源。另外,規(guī)范要求,對(duì)那些可能對(duì)服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請(qǐng)求方法(特別是 GET 以外的 HTTP 請(qǐng)求,或者搭配某些 MIME 類型的 POST 請(qǐng)求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求(preflight request),從而獲知服務(wù)端是否允許該跨域請(qǐng)求。服務(wù)器確認(rèn)允許之后,才發(fā)起實(shí)際的 HTTP 請(qǐng)求。在預(yù)檢請(qǐng)求的返回中,服務(wù)器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies和 HTTP 認(rèn)證相關(guān)數(shù)據(jù))。
CORS需要瀏覽器和服務(wù)器同時(shí)支持,瀏覽器需要 ie8 以上。
瀏覽器端的寫法:
function CORSRequest(method,url,opation,callback) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // 此時(shí)即支持CORS的情況 // 檢查XMLHttpRequest對(duì)象是否有“withCredentials”屬性 // “withCredentials”僅存在于XMLHTTPRequest level 2對(duì)象里 } else { // 否則檢查是否支持XDomainRequest // XDomainRequest僅存在于IE中,是IE8 和 IE9 用于支持CORS請(qǐng)求的方式 xhr = new XDomainRequest(); } xhr.open(method, url, true); if(method=="POST"){ //可以設(shè)置不同請(qǐng)求方法的操作,所有的請(qǐng)求都可以分別設(shè)置 xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); //設(shè)置請(qǐng)求頭 xhr.send(opation); //傳輸數(shù)據(jù)到服務(wù)器 }else{ xhr.send(); } xhr.onload = function(){ callback(xhr.responseText); } }; function notice(data) { console.log(data) } CORSRequest("POST","http://example.com","傳遞給服務(wù)器的數(shù)據(jù)",notice);
服務(wù)器端的寫法:
Apache:
Apache需要使用 mod_headers 模塊來激活 HTTP 頭的設(shè)置,它默認(rèn)是激活的。你只需要在 Apache 配置文件的, , 或的配置里加入以下內(nèi)容即可:
Header set Access-Control-Allow-Origin *
PHP:
以上的配置的含義是允許任何域發(fā)起的請(qǐng)求都可以獲取當(dāng)前服務(wù)器的數(shù)據(jù)。當(dāng)然,這樣有很大的危險(xiǎn)性,惡意站點(diǎn)可能通過XSS攻擊我們的服務(wù)器。所以我們應(yīng)該盡量有針對(duì)性的對(duì)限制安全的來源,例如:
?CORS 的優(yōu)點(diǎn):
CORS與JSONP相比,無疑更為先進(jìn)、方便和可靠。
JSONP 只能實(shí)現(xiàn) GET 請(qǐng)求,而 CORS 支持所有類型的HTTP請(qǐng)求。
使用 CORS,開發(fā)者可以使用普通的 XMLHttpRequest 發(fā)起請(qǐng)求和獲得數(shù)據(jù),擁有onerror 和 onabort 方法,比起 JSONP 有更好的錯(cuò)誤處理。
JSONP 主要被老的瀏覽器支持,它們往往不支持 CORS,而絕大多數(shù)現(xiàn)代瀏覽器都已經(jīng)支持了 CORS。
注意點(diǎn):
當(dāng)發(fā)送的請(qǐng)求是非簡(jiǎn)單請(qǐng)求,瀏覽器會(huì)在正式通信之前,增加一次 HTTP 查詢請(qǐng)求,稱為"預(yù)檢"請(qǐng)求( preflight )。瀏覽器先詢問服務(wù)器,當(dāng)前網(wǎng)頁所在的域名是否在服務(wù)器的許可名單之中,以及可以使用哪些 HTTP 動(dòng)詞和頭信息字段。只有得到肯定答復(fù),瀏覽器才會(huì)發(fā)出正式的 XMLHttpRequest 請(qǐng)求,否則就報(bào)錯(cuò)。
一般前端框架比如"ExtJS"、"AngularJS", 框架監(jiān)測(cè)到訪問的域名可能存在跨域的話會(huì)先發(fā)送一個(gè) OPTIONS 請(qǐng)求,驗(yàn)證是否可進(jìn)行通信,如果返回可通信才會(huì)真正發(fā)起一個(gè) POST、GET 請(qǐng)求。
下圖是框架發(fā)起的 OPTIONS 請(qǐng)求,當(dāng)如果服務(wù)器的 Nginx 并沒有設(shè)置允許跨域請(qǐng)示的時(shí)候,它會(huì)返回一個(gè) 405 狀態(tài)碼。
axios 框架也會(huì)發(fā)送 OPTIONS 請(qǐng)求。
關(guān)于 CROS 的更多內(nèi)容,可以進(jìn)入以下鏈接了解:
https://developer.mozilla.org...
http://www.ruanyifeng.com/blo...
方法三:JSONP在 js 中,我們直接用 XMLHttpRequest 請(qǐng)求不同域上的數(shù)據(jù)時(shí),是不可以的。但是,在頁面上引入不同域上的 js 腳本文件卻是可以的,jsonp 正是利用這個(gè)特性來實(shí)現(xiàn)的。
JSONP是一種非正式傳輸協(xié)議,該協(xié)議的一個(gè)要點(diǎn)就是允許用戶傳遞一個(gè)約定的參數(shù)(一般是 callback )給服務(wù)端,然后服務(wù)端返回?cái)?shù)據(jù)時(shí)會(huì)將這個(gè) callback 參數(shù)作為函數(shù)名來包裹住JSON數(shù)據(jù),這樣客戶端就可以隨意定制自己的函數(shù)來自動(dòng)處理返回?cái)?shù)據(jù)了。
比如,有個(gè) a.html 頁面,它里面的代碼需要利用 ajax 獲取一個(gè)不同域上的 json 數(shù)據(jù),假設(shè)這個(gè) json 數(shù)據(jù)地址是 http://example.com/data.php , 那么 a.html 中的代碼就可以這樣:
我們看到獲取數(shù)據(jù)的地址后面還有一個(gè) callback 參數(shù),按慣例是用這個(gè)參數(shù)名,但是你用其他的也一樣。當(dāng)然如果獲取數(shù)據(jù)的jsonp地址頁面不是你自己能控制的,就得按照提供數(shù)據(jù)的那一方的規(guī)定格式來操作了。關(guān)鍵是參數(shù)值必須是當(dāng)前頁面 script 設(shè)置的數(shù)據(jù)處理回調(diào)函數(shù)的名字。
因?yàn)槭钱?dāng)做一個(gè)js文件來引入的,所以http://example.com/data.php 返回的必須是一個(gè)能執(zhí)行的js文件,所以這個(gè)頁面的php代碼可能是這樣的:
最終那個(gè)頁面輸出的結(jié)果是:
所以通過 http://example.com/data.php?c... 得到的 js 文件,就是我們之前定義的 dosomething 函數(shù),并且它的參數(shù)就是我們需要的 json 數(shù)據(jù),這樣我們就跨域獲得了我們需要的數(shù)據(jù)。
這樣 jsonp 的原理就很清楚了,通過 script 標(biāo)簽引入一個(gè) js 文件,這個(gè) js 文件載入成功后會(huì)執(zhí)行我們?cè)?url 參數(shù)中指定的函數(shù),并且會(huì)把我們需要的 json 數(shù)據(jù)作為參數(shù)傳入。所以 jsonp 是需要服務(wù)器端的頁面進(jìn)行相應(yīng)的配合的。
知道 jsonp 跨域的原理后我們就可以用 js 動(dòng)態(tài)生成 script 標(biāo)簽來進(jìn)行跨域操作了,而不用特意的手動(dòng)的書寫那些 script 標(biāo)簽。
一個(gè)跨域獲取淘寶關(guān)鍵字搜索建議的例子:
效果:
優(yōu)點(diǎn):
不受同源策略的限制;
兼容性更好,在更加古老的瀏覽器中都可以運(yùn)行,不需要XMLHttpRequest或ActiveX的支持;
請(qǐng)求完畢后可以通過調(diào)用callback的方式回傳結(jié)果。
缺點(diǎn):
只支持GET請(qǐng)求而不支持POST等其它類型的HTTP請(qǐng)求;
只支持跨域HTTP請(qǐng)求這種情況,不能解決不同域的兩個(gè)頁面之間如何進(jìn)行JavaScript調(diào)用的問題。
方法四:設(shè)置代理服務(wù)器例如 www.123.com/index.html 需要調(diào)用 www.456.com/server.php,可以寫一個(gè)接口 www.123.com/server.php ,由這個(gè)接口在后端去調(diào)用 www.456.com/server.php 并拿到返回值,然后再返回給 index.html,這就是一個(gè)代理的模式。相當(dāng)于繞過了瀏覽器端,自然就不存在跨域問題。
例如:
a.php 后臺(tái)獲取跨域的數(shù)據(jù)
?前端只要將 XMLHttpRequest 的請(qǐng)求地址設(shè)置為 "a.php" 就可以。
方法五:使用 window.namewindow 對(duì)象有個(gè) name 屬性,該屬性有個(gè)特征:即在一個(gè)窗口( window )的生命周期內(nèi),窗口載入的所有的頁面都是共享一個(gè) window.name 的,每個(gè)頁面對(duì) window.name 都有讀寫的權(quán)限,window.name 是持久存在一個(gè)窗口載入過的所有頁面中的,并不會(huì)因新頁面的載入而進(jìn)行重置。
比如:有一個(gè)頁面 a.html,它里面有這樣的代碼:
再看看 b.html 頁面的代碼:
a.html 頁面載入后 3 秒,跳轉(zhuǎn)到了 b.html 頁面,結(jié)果為:
我們看到在 b.html 頁面上成功獲取到了它的上一個(gè)頁面 a.html 給 window.name 設(shè)置的值。如果在之后所有載入的頁面都沒對(duì) window.name 進(jìn)行修改的話,那么所有這些頁面獲取到的 window.name 的值都是 a.html 頁面設(shè)置的那個(gè)值。當(dāng)然,如果有需要,其中的任何一個(gè)頁面都可以對(duì) window.name 的值進(jìn)行修改。注意,window.name 的值只能是字符串的形式,這個(gè)字符串的大小最大能允許 2M 左右甚至更大的一個(gè)容量,具體取決于不同的瀏覽器,但一般是夠用了。
上面的例子中,我們用到的頁面 a.html 和 b.html 是處于同一個(gè)域的,但是即使 a.html 與 b.html 處于不同的域中,上述結(jié)論同樣是適用的,這也正是利用 window.name 進(jìn)行跨域的原理。
此方法需要與 iframe 配合使用。
比如有一個(gè) www.example.com/a.html 頁面,需要通過 a.html 頁面里的 js 來獲取另一個(gè)位于不同域上的頁面 www.cnblogs.com/data.html 里的數(shù)據(jù)。
data.html 頁面里的代碼很簡(jiǎn)單,就是給當(dāng)前的 window.name 設(shè)置一個(gè) a.html 頁面想要得到的數(shù)據(jù)值。data.html 里的代碼:
那么在 a.html 頁面中,我們?cè)趺窗?data.html 頁面載入進(jìn)來呢?顯然我們不能直接在 a.html 頁面中通過改變 window.location 來載入 data.html 頁面,因?yàn)槲覀兿胍词?a.html 頁面不跳轉(zhuǎn)也能得到 data.html 里的數(shù)據(jù)。答案就是在 a.html 頁面中使用一個(gè)隱藏的 iframe 來充當(dāng)一個(gè)中間人角色,由 iframe 去獲取 data.html 的數(shù)據(jù),然后 a.html 再去得到iframe獲取到的數(shù)據(jù)。
充當(dāng)中間人的 iframe 想要獲取到 data.html 的通過 window.name 設(shè)置的數(shù)據(jù),只需要把這個(gè) iframe 的 src 設(shè)為 www.cnblogs.com/data.html 就行了。然后 a.html 想要得到 iframe 所獲取到的數(shù)據(jù),也就是想要得到 iframe的window.name 的值,還必須把這個(gè) iframe 的 src 設(shè)成跟 a.html 頁面同一個(gè)域才行,不然根據(jù)前面講的同源策略,a.html 是不能訪問到 iframe 里的 window.name 屬性的。這就是整個(gè)跨域過程。
看下 a.html 頁面的代碼:
上面的代碼只是最簡(jiǎn)單的原理演示代碼,你可以對(duì)使用 js 封裝上面的過程,比如動(dòng)態(tài)的創(chuàng)建 iframe ,動(dòng)態(tài)的注冊(cè)各種事件等等,當(dāng)然為了安全,獲取完數(shù)據(jù)后,還可以銷毀作為代理的 iframe。
方法六:使用 window.postMessagewindow.postMessage(message,targetOrigin) 方法是 html5 新引進(jìn)的特性,可以使用它來向其它的 window 對(duì)象發(fā)送消息,無論這個(gè) window 對(duì)象是屬于同源或不同源,目前 IE8+、FireFox、Chrome、Opera 等瀏覽器都已經(jīng)支持 window.postMessage 方法。
調(diào)用 postMessage 方法的 window 對(duì)象是指要接收消息的那一個(gè) window 對(duì)象,該方法的第一個(gè)參數(shù) message 為要發(fā)送的消息,類型只能為字符串;第二個(gè)參數(shù) targetOrigin 用來限定接收消息的那個(gè) window 對(duì)象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的 window對(duì) 象,可是通過監(jiān)聽自身的 message 事件來獲取傳過來的消息,消息內(nèi)容儲(chǔ)存在該事件對(duì)象的 data 屬性中。
上面所說的向其他 window 對(duì)象發(fā)送消息,其實(shí)就是指一個(gè)頁面有幾個(gè)框架的那種情況,因?yàn)槊恳粋€(gè)框架都有一個(gè) window 對(duì)象。下面看一個(gè)簡(jiǎn)單的示例,有兩個(gè)頁面
我們運(yùn)行 a 頁面后得到的結(jié)果:
我們看到 b 頁面成功的收到了消息。
使用 postMessage 來跨域傳送數(shù)據(jù)還是比較直觀和方便的,但是缺點(diǎn)是 IE6、IE7 不支持,所以用不用還得根據(jù)實(shí)際需要來決定。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/54468.html
摘要:由于瀏覽器的同源策略保護(hù)機(jī)制,瀏覽器不能執(zhí)行來自其他來源的腳本。然后想要得到所獲取到的數(shù)據(jù),也就是想要得到的的值,還必須把這個(gè)的設(shè)成跟頁面同一個(gè)域才行,不然根據(jù)前面講的同源策略,是不能訪問到里的屬性的。 由于瀏覽器的同源策略保護(hù)機(jī)制,瀏覽器不能執(zhí)行來自其他來源的腳本。通過 js 在不同的域之間進(jìn)行數(shù)據(jù)傳輸或通信,比如用 ajax 向一個(gè)不同的域請(qǐng)求數(shù)據(jù),或者通過 js 獲取頁面中不同域...
摘要:前言由于自己平時(shí)只做做,并沒有遇到太多跨域問題,今天通過幾個(gè)樣例模擬實(shí)現(xiàn)了幾種跨域方式。 前言 由于自己平時(shí)只做做demo,并沒有遇到太多跨域問題,今天通過幾個(gè)樣例模擬實(shí)現(xiàn)了幾種跨域方式。原文地址 傳送門 本文所有樣例靜態(tài)服務(wù)器基于nodejs實(shí)現(xiàn),代碼親測(cè)可用。測(cè)試步驟如下: 1.為了實(shí)現(xiàn)跨域訪問的效果,需要下載http-server 作為一個(gè)服務(wù)器 npm install http...
摘要:前言騰訊一面,相比阿里一面來說,騰訊一面先給打電話預(yù)定時(shí)間,這也給了我們這些面試者去準(zhǔn)備的時(shí)間。其實(shí)閉包也就是指有權(quán)訪問另一個(gè)函數(shù)作用域的函數(shù)而已。常用的創(chuàng)建閉包的方法就是在函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。 前言 騰訊一面,相比阿里一面來說,騰訊一面先給打電話預(yù)定時(shí)間,這也給了我們這些面試者去準(zhǔn)備的時(shí)間。但是也正是因?yàn)檫@種確定性,也有在等待電話的時(shí)候的心情的忐忑。 背景 我是一名大三學(xué)生,大一...
摘要:在中,在不同的域名下面進(jìn)行數(shù)據(jù)交互,就會(huì)遇到跨域問題,說到跨域首先要從同源說起,瀏覽器為了提供一種安全的運(yùn)行環(huán)境,各個(gè)瀏覽器廠商協(xié)定使用同源策略。在上面說過是不受同源策略限制的,但是出于安全原因,瀏覽器限制從腳本內(nèi)發(fā)起的跨源請(qǐng)求。 對(duì)于前端開發(fā)來說跨域應(yīng)該是最不陌生的問題了,無論是開發(fā)過程中還是在面試過程中都是一個(gè)經(jīng)常遇到的一個(gè)問題,在開發(fā)過程中遇到這個(gè)問題的話一般都是找后端同學(xué)去解決...
閱讀 2410·2021-10-14 09:43
閱讀 2443·2021-09-09 09:34
閱讀 1606·2019-08-30 12:57
閱讀 1207·2019-08-29 14:16
閱讀 725·2019-08-26 12:13
閱讀 3208·2019-08-26 11:45
閱讀 2290·2019-08-23 16:18
閱讀 2669·2019-08-23 15:27