摘要:例外當(dāng)涉及到同源策略時,有兩個主要的例外授信范圍兩個相互之間高度互信的域名,如公司域名,不遵守同源策略的限制。端口未將端口號加入到同源策略的組成部分之中,因此和屬于同源并且不受任何限制。
原文鏈接:http://www.devsai.com/2016/11/24/talk-CORS/
同源策略(same origin policy)1995年,同源政策由 Netscape 公司引入瀏覽器。為了防止某些文檔或腳本加載別的域下的未知內(nèi)容,防止造成泄露隱私,破壞系統(tǒng)等行為發(fā)生。
同源策略做了兩種限制:
不能通過ajax的方法或其他腳本中的請求去訪問不同源中的文檔。
瀏覽器中不同域的框架之間是不能進(jìn)行js的交互操作的。
現(xiàn)在所有的可支持javascript的瀏覽器都會使用這個策略。
怎么算同源URL的三部分完全相同時我們就可以稱其為同源,這三部分是: 協(xié)議,域名(主機(jī)名)和端口都相同。
IE 例外當(dāng)涉及到同源策略時,Internet Explorer有兩個主要的例外
授信范圍(Trust Zones):兩個相互之間高度互信的域名,如公司域名(corporate domains),不遵守同源策略的限制。 端口:IE未將端口號加入到同源策略的組成部分之中,因此 http://company.com:81/index.html 和http://company.com/index.html 屬于同源并且不受任何限制。跨域的幾種解決方法
雖然同源策略很有必要,但有很多時候我們還是需要去請求其他域的數(shù)據(jù),如:調(diào)用不同業(yè)務(wù)的數(shù)據(jù),而不同業(yè)務(wù)已子域區(qū)分;又或者是第三方公用的數(shù)據(jù)接口等等
由于各種原因,我們需要通過各種方式來請求到不同域下的資源。
jsonpjsonp是通過可以發(fā)出跨域請求的script標(biāo)簽,使javascript能夠獲得跨域請求的數(shù)據(jù),并調(diào)用數(shù)據(jù)。
先看個例子:
文件index.js :
alert(123);
頁面index.html:
... ...
當(dāng)加載頁面index.html后,出123內(nèi)容的彈窗。通過查看index.js的響應(yīng)體,會發(fā)現(xiàn)響應(yīng)內(nèi)容就是alert(123)。
所以,可以這么思考,只要是通過script標(biāo)簽請求到的內(nèi)容就會被當(dāng)做js代碼執(zhí)行。
是否可以在script中的地址src不請求js文件,而是請求服務(wù)端的接口(即使不在同源下的),那么返回的內(nèi)容就能獲得到,并且會當(dāng)成js代碼來執(zhí)行。(一般的script標(biāo)簽都會去請求js代碼文件)
再來看下正常的服務(wù)端獲取數(shù)據(jù)接口。
比如:有這么個接口/getUserInfo/001,通過ajax請求獲得此接口數(shù)據(jù){"data" : {"name" : "devsai",like:"everything"}}。
得到數(shù)據(jù)后在ajax中調(diào)用showUserInfo(data)來渲染頁面,data就是接口數(shù)據(jù)。
如果現(xiàn)在用script標(biāo)簽來請求數(shù)據(jù),那么同樣可以獲得數(shù)據(jù),執(zhí)行返回到的內(nèi)容,因是json格式的數(shù)據(jù),并不會報錯,但也并沒有卵用。獲得接口的數(shù)據(jù)肯定是想做些什么的。
再想想,正常ajax請求后的js執(zhí)行內(nèi)容showUserInfo(data),拿到數(shù)據(jù)后,調(diào)用了showUserInfo函數(shù)。
那么,用script標(biāo)簽來請求數(shù)據(jù)時,返回的內(nèi)容直接是showUserInfo(data)不就行了,但服務(wù)端又不知道我們到底要執(zhí)行哪個函數(shù),即使事先約定了,但后面因某些事要改,那還得告訴服務(wù)端,太麻煩了。
如果知道要執(zhí)行什么函數(shù)就好了。
當(dāng)然,這是可以的,改造下接口,以參數(shù)的形式把函數(shù)名傳給服務(wù)端。
Response返回的內(nèi)容同樣需要改造
Response: showUserInfo({"data" : {"name" : "devsai",like:"everything"}})
這樣,通過jsonp,去跨域請求接口數(shù)據(jù)就完成了。
需要注意的是函數(shù)名需要掛在window下面,要不然會報函數(shù)名未定義。
例如在demo.devsai.com/index.html頁面里執(zhí)行如下內(nèi)容:
document.domain = "devsai.com";
執(zhí)行該語句后,可以成功通過devsai.com/index.html的同源檢測, 實現(xiàn)數(shù)據(jù)的通訊,
當(dāng)然document.domain不能隨意設(shè)置,只能設(shè)置成當(dāng)前域,或設(shè)置成當(dāng)前域的頂域。
document.domain常常被用于同站但不同域的情況,例如:www.devsai.com,下嵌入了iframe廣告頁面ad.devsai.com,想要實現(xiàn)兩頁面的通訊,就需要對兩個頁面都設(shè)置document.domain="devsai.com"。
window.namewindow對象有個name屬性,該屬性有個特征:即在一個窗口(window)的生命周期內(nèi),窗口載入的所有的頁面都是共享一個window.name的,每個頁面對window.name都有讀寫的權(quán)限,window.name是持久存在一個窗口載入過的所有頁面中的,并不會因新頁面的載入而進(jìn)行重置。
name只能是字符串。
頁面a.html中:
頁面b.html:
再來看看如何讓a.html頁面獲取數(shù)據(jù)
用data.html作為請求數(shù)據(jù)地址:
Data
a.html:
... ...
當(dāng)訪問http://127.0.0.1:8080/static/index.html,便能獲得來自不同域下data.html中的數(shù)據(jù)。
也可以做的更完善些,動態(tài)的生成iframe請求數(shù)據(jù),用完即毀。
.... // 傳入請求數(shù)據(jù)接口地址和回調(diào)函數(shù) function requestData(url,successCB){ var body = document.getElementsByTagName("body")[0]; var iframe = document.createElement("iframe"); iframe.setAttribute("id", "getDataByWindowName"); iframe.setAttribute("width", "0"); iframe.setAttribute("height", "0"); iframe.setAttribute("border", "0"); iframe.setAttribute("style", "width: 0; height: 0; border: none;"); iframe.setAttribute("src", url); body.appendChild(iframe); setTimeout(function(){//防止iframe.src在沒加載前就被替換 iframe.onload = function(){ var data = iframe.contentWindow.name; if(data){ data = JSON.parse(data);//轉(zhuǎn)成 JSON successCB && successCB(data); } iframe.parentNode.removeChild(iframe); } iframe.src = "about:blank"; }, 100); } //requestData("http://localhost:8080/static/data.html",showUserInfo); ...
這就是使用window.name來進(jìn)行跨域。
window.postMessagewindow.postMessage方法是html5的新特性之一,
可以使用它來向其它的window對象發(fā)送消息,不管這個window對象是屬于同源或不同源。
通過window.postMessage允許瀏覽器windows, tabs, and iFrames之間跨域通訊。
之前寫過一篇關(guān)于window.postMessage的,做了詳細(xì)的說明+演示頁面+演示代碼,去看看
服務(wù)端地址映射例如一個網(wǎng)站上有各種不同的業(yè)務(wù),不同的業(yè)務(wù)有其對應(yīng)的子域。
如:ad.devsai.com;upload.devsai.com;live.devsai.com,分別對應(yīng)廣告業(yè)務(wù),上傳業(yè)務(wù),直播業(yè)務(wù)。
想在www.devsai.com中做交互,或獲得數(shù)據(jù),便會受跨域影響。
造成跨域的原因是因為請求數(shù)據(jù)的源不同,那只要請求的源一樣,便沒有跨域問題了。
這也是可以辦到的,只需要web服務(wù)做下代理,或稱之為地址映射。
拿Nginx舉例,需要在web服務(wù)上做如下配置:
... lcaotion /ad { proxy_pass http://ad.devsai.com } location /upload { proxy_pass http://upload.devsai.com } location /live { proxy_pass http://live.devsai.com } ...
然后就可以在以www.devsai.com/ad/的方式去調(diào)用廣告業(yè)務(wù)。
CORS跨域資源共享當(dāng)一個發(fā)起的請求地址與發(fā)起該請求本身所在的地址不在同源下時,稱該請求發(fā)起了一個跨域的HTTP請求。
有些的跨域請求是被允許的,,圖片,腳本,樣式及其他資源 ,加載這些數(shù)據(jù)時即使不在同源下面也同樣被允許,如今的網(wǎng)站通常也會去引用不在同源下的這些資源,如做CDN加速。
但也有些不被允許,正如大家所知,出于安全考慮,瀏覽器會“限制”腳本中發(fā)起的跨站請求,比如:XmlHttpRequest。
除了XmlHttpRequest外,還有以下幾種跨域請求做了相應(yīng)的安全限制。
比如:
1 前面說的iframe,通過設(shè)置src可以發(fā)起跨域請求,但對請求到的內(nèi)容進(jìn)行操作就不被允許了。如執(zhí)行iframe.contentWindow.name就會報錯。
2 標(biāo)簽上crossorigin屬性是一個CORS的配置屬性,目的是為了允許第三方網(wǎng)站上的圖片(即不在同源上的圖片)能夠在canvas中被使用。
如果沒有配置改屬性,又跨域請求了圖片,當(dāng)調(diào)用canvas中的toBlob(), toDataURL(), 或getImageData()方法的時候,會報錯。
var img = new Image, canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"), src = "https://sf-sponsor.b0.upaiyun.com/45751d8fcd71e4a16c218e0daa265704.png"; // insert image url here img.crossOrigin = "Anonymous"; img.onload = function() { console.log(img); canvas.width = img.width; canvas.height = img.height; ctx.drawImage( img, 0, 0 ); localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") ); } img.src = src;
3 同樣的script也可以有crossorigin屬性,
script本身沒有跨域問題,不然jsonp就沒法用了。但如果請求的不是同源下的js文件,發(fā)生錯誤后,無法通過window.onerror事件捕捉到詳細(xì)的信息
例如加載index.js文件,其中a未定義:
var b = a;
同源下的 window.onerror報錯信息
跨域下的 window.onerror報錯信息
通過script標(biāo)簽上添加crossdomain屬性,并在服務(wù)上配置響應(yīng)頭。
在去看onerror中的報錯信息就和同源下的報錯信息一樣了。
4 Web字體 (CSS 中通過 @font-face 使用跨站字體資源),使用非同源地址,同樣會報錯。
還需要注意的一點是,跨域請求并非是瀏覽器限制了請求,而是瀏覽器攔截了返回結(jié)果。不管是否跨域,請求都會發(fā)送到服務(wù)端。
但也有特例,有些瀏覽器不允許從HTTPS的域跨域訪問HTTP,比如Chrome和Firefox,這些瀏覽器在請求還未發(fā)出的時候就會攔截請求。
解決這類跨域問題的方法就是*CORS*,對于簡單的請求來說,前端這邊都不需要做任何的編碼就能實現(xiàn)跨域請求,
只需要服務(wù)端配置響應(yīng)頭"Access-Control-Allow-Origin:*"。
CORS是一個W3C標(biāo)準(zhǔn),全稱“跨域資源共享”(Cross-origin resource sharing)
跨源資源共享標(biāo)準(zhǔn)通過新增一系列 HTTP 頭,讓服務(wù)器能聲明哪些來源可以通過瀏覽器訪問該服務(wù)器上的資源。
CORS服務(wù)端設(shè)置(Set Response Header)Access-Control-Allow-Origin
根據(jù)Reuqest請求頭中的Origin來判斷該請求的資源是否可以被共享。
如果Origin指定的源,不在許可范圍內(nèi),服務(wù)器會返回一個正常的HTTP回應(yīng)。瀏覽器發(fā)現(xiàn),這個回應(yīng)的頭信息沒有包含Access-Control-Allow-Origin字段(該字段的值為服務(wù)端設(shè)置Access-Control-Allow-Origin的值)便知出錯了,從而拋出一個錯誤,被XMLHttpRequest的onerror回調(diào)函數(shù)捕獲。此時HTTP的返回碼為200,所以 這種錯誤無法通過狀態(tài)碼識別。
Access-Control-Allow-Credentials
指定是否允許請求帶上cookies,HTTP authentication,client-side SSL certificates等消息。
如需要帶上這些信息,Access-Control-Allow-Credentials:true并需要在XmlHpptRequest中設(shè)置xhr.withCredentials=true。
需注意的是,當(dāng)設(shè)置了the credentials flag為true,那么Access-Control-Allow-Origin就不能使用"*"
Access-Control-Max-Age
可選字段,指定了一個預(yù)請求將緩存多久,在緩存失效前將不會再發(fā)送預(yù)請求。
Access-Control-Allow-Methods
作為預(yù)請求Response的一部分,指定了真實請求可以使用的請求方式。
Access-Control-Allow-Headers
作為預(yù)請求Response的一部分,指定了真實請求可以使用的請求頭名稱(header field names)。
CORS兩種請求方式CORS的有兩種請求方式: 簡單請求(Simple Request) 和 預(yù)請求(Prefilght Request)
簡單請求(Simple Request)只要同時滿足以下三大條件,就屬于簡單請求。
a) 請求方式是以下幾種方式之一
* GET * POST * HEAD
b) content-type必須是以下幾種之一
* application/x-www-form-urlencoded * multipart/form-data * text/plain
c) 不會使用自定義請求頭(類似于 X-Modified 這種)。
預(yù)請求(Prefilght Request)如果不滿足簡單請求的三大條件,會在發(fā)送正真的請求前,發(fā)送個請求方式為"OPTIONS"的請求,去服務(wù)端做檢測,
1) 請求方式不是GET,POST,HEAD
那么需要在響應(yīng)HEAD配置允許的請求方式,例如:Access-Control-Allow-Methods:PUT,DELETE
2) 使用自定義請求頭,如x-devsai ,那么需要在服務(wù)端相應(yīng)配置允許的自定義請求頭:Access-Control-Request-Headers: x-devsai
一旦檢測不通過,瀏覽器就會提示相應(yīng)的報錯,并不會發(fā)生真實的請求。
CORS兼容性從上圖可只IE11,以下的就不支持CORS了。但實際上再IE8,IE9,IE10中,可以用XDomainRequest對象代替XmlHttpReuqest,發(fā)送跨域請求。
var xdr = new XDomainRequest(); xdr.open("get", "http://www.devsai.com/xdr"); xdr.send();結(jié)語
最后,總結(jié)下各種跨域方案的特點,還記得本文開始說的,同源策略的兩種限制嗎?
不能通過__ajax的方法__或__其他腳本中的請求__去訪問不同源中的文檔。
瀏覽器中不同域的框架之間是不能進(jìn)行js的交互操作的。
把第1種標(biāo)記為__TYPE_1__,第二種標(biāo)記為__TYPE_2__,對上述的幾種解決跨域的方法分下類。
window.name 需要注意name只能是字符串
解決的限制 :__TYPE_1__,__TYPE_2__
缺點: 接口返回的內(nèi)容必須都是html里嵌入script腳本。
document.domain 通過修改domain跨子域
解決的限制 :__TYPE_2__
缺點: 僅支持同個域下的子域跨域,跨域能力有限
window.postMessage 用于iframe、window、tabs之間的跨域通訊
解決的限制 :__TYPE_2__
缺點: 兼容問題,IE10以下受限,IE8以下無效
jsonp 是之前最常用的解決跨域請求的方法。
解決的限制 :__TYPE_1__
缺點: 不能用于POST請求
服務(wù)端地址映射 前端不需要管,并能解決跨域請求問題的一種方法。
解決的限制 :__TYPE_1__
缺點: 非要說缺點,那就是要說服服務(wù)端同學(xué),而且一般場子鋪大了的公司只用同源,不太可能。
CORS 感覺目前比較常用的解決跨域請求的方法。
解決的限制 :__TYPE_1__
缺點: 也是兼容性問題
真正開發(fā)過程中,需針對不同情況,使用不同的解決之法。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/81011.html
摘要:為了防止某些文檔或腳本加載別的域下的未知內(nèi)容,防止造成泄露隱私,破壞系統(tǒng)等行為發(fā)生。模式構(gòu)建函數(shù)響應(yīng)式前端架構(gòu)過程中學(xué)到的經(jīng)驗?zāi)J降牟煌幵谟冢饕獙W⒂谇‘?dāng)?shù)貙崿F(xiàn)應(yīng)用程序狀態(tài)突變。嚴(yán)重情況下,會造成惡意的流量劫持等問題。 今天是編輯周刊的日子。所以文章很多和周刊一樣。微信不能發(fā)鏈接,點了也木有用,所以請記得閱讀原文~ 發(fā)個動圖娛樂下: 使用 SVG 動畫制作游戲 使用 GASP ...
摘要:可以說同源策略在安全中扮演著及其重要的角色。我把這個領(lǐng)域的東西寫成了一個系列,以后還會繼續(xù)完善下去安全一同源策略與跨域安全二攻擊安全三攻擊 之所以要將同源策略與跨域?qū)懺谝黄穑且驗榇嬖跒g覽器的同源策略,才會存在跨域問題 何為同源策略 同源策略是瀏覽器實現(xiàn)的一種安全策略,它限制了不同源之間的文檔和腳本交互的權(quán)限。只有同一個源的腳本才會具有操作dom、讀寫cookie、session 、a...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個重要的知識點同源策略什么是同源策略出于保護(hù)用戶信息安全的目的,現(xiàn)在的瀏覽器都會實施同源策略這個政策,所謂同源策略指的是不同源的客戶端腳本在沒有明確授權(quán)情況下,不允許讀寫對方的資源。 導(dǎo)語你家的小孩帶了他的朋友來你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:扯了這么多,自然不是為了吹水,而是要為了引出前端開發(fā)的一個重要的知識點同源策略什么是同源策略出于保護(hù)用戶信息安全的目的,現(xiàn)在的瀏覽器都會實施同源策略這個政策,所謂同源策略指的是不同源的客戶端腳本在沒有明確授權(quán)情況下,不允許讀寫對方的資源。 導(dǎo)語你家的小孩帶了他的朋友來你們的家里玩,你家的小孩如果要在自家屋里拿玩具玩、拿東西吃你自然是不會阻止,但是如果你家小孩的朋友人品不行,亂拿東西吃、...
摘要:由于瀏覽器的同源策略導(dǎo)致無法直接通過拿到后臺數(shù)據(jù)。目前,如果非同源,共有三種行為受到限制。此處應(yīng)有掌聲參考關(guān)于跨域資源共享和瀏覽器的同源策略限制的具體講解。 工作中,經(jīng)常會遇到需要跨域請求數(shù)據(jù)的情況。由于瀏覽器的同源策略,導(dǎo)致無法直接通過ajax拿到后臺數(shù)據(jù)。解決這個問題的方式之一就是JSONP。還有一種方式更高效簡單——跨域資源共享(Cross-origin Resource Sha...
閱讀 3163·2021-09-30 09:47
閱讀 2022·2021-09-22 16:04
閱讀 2289·2021-09-22 15:44
閱讀 2547·2021-08-25 09:38
閱讀 547·2019-08-26 13:23
閱讀 1234·2019-08-26 12:20
閱讀 2818·2019-08-26 11:59
閱讀 1085·2019-08-23 18:40