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