摘要:于是乎同源策略應運而生主要限制在于和無法讀取。怎么繞過同源策略首先一般來說協議和端口造成的跨域問題大部分方法是沒有辦法繞過的。二級域名是寄存在主域名之下的域名。當主域名受到懲罰二級域名也會連帶懲罰。
前言
這是一道前端跨不過躲不掉面試必備的知識,掙扎多年沒能做到刻骨銘心深入脊髓,只能好好寫篇博文記錄起來了;
什么是跨域?廣義來說,A域執行的文檔腳本試圖去請求B域下的資源是不被允許的,源于瀏覽器的同源策略,所謂同源指的是協議,域名 (domain) ,端口都相同,例如下面四個URL就屬于不同源;
舉例URL: http://www.a.com(:80)/index.html
協議不同: https://www.a.com(:80)/index.html
域名不同: http://www.b.com(:80)/index.html
端口不同: http://www.a.com(:8080)/index.html
目的就是為了保證用戶信息的安全,阻止和減少通過惡意分子竊取數據(例如普遍的CSRF攻擊)。
首先同源策略也分兩種XmlHttpRequest同源策略和DOM同源策略,
我們常說的同源策略一般是指XmlHttpRequest同源策略;
例如用戶登陸某個購物平臺;
用戶登陸www.shopping.com購物頁,用戶信息被保存在cookie中;
用戶被引導進惡意詐騙頁面www.spite.com,執行了當中的惡意代碼請求www.shopping.com,默認會發送對應cookie信息;
www.shopping.com驗證通過,返回請求數據,這時候相當于賬戶信息和權限都在他人手中了
這一過程不為用戶所知道,這是屬于XmlHttpRequest同源策略,總的來說就是禁止使用XHR對象向不同源的服務器地址發起HTTP請求。
至于DOM同源策略也是差不多道理
用戶被引導進惡意詐騙頁面www.spite.com,它用iframe偽裝成www.shopping.com購物頁;
用戶登陸賬號密碼,惡意詐騙頁面就能拿用戶信息跨域訪問www.shopping.com的DOM節點;
這一過程不為用戶所知道,這是屬于DOM同源策略,總的來說就是禁止對不同源頁面DOM進行操作。
于是乎同源策略應運而生,主要限制在于
Cookie、LocalStorage 和 IndexDB 無法讀取。
DOM 無法獲得。
AJAX 請求不能發送。
需要注意的是同源策略只對網頁的HTML文檔做了限制,對加載的其他靜態資源如javascript、css、圖片等仍然認為屬于同源。
同源策略是絕對的么?既然同源策略存在的重要性不言而喻,為什么我們需要嘗試繞過這道坎?
例如銀行的安全性夠高了吧,相對應的限制性也是極高的,同理可得安全往往也是犧牲了部分靈活自由的。今時今日的互聯網人流量,如果大型網站全部文件都只能訪問指定一臺服務器,根本不可能,也絕對支撐不起雙十一的電商網站用戶流量;于是在遵循同源策略的基礎下,瀏覽器也適當地給開發者留下一些“密道”。
首先,一般來說協議和端口造成的跨域問題大部分方法是沒有辦法繞過的。
然后我們認識一下強大的跨域神器
IFrame 對象IFrame 對象代表一個 HTML 的內聯框架,由于它是獨立的頁面,因而擁有自己的事件,擁有自己的窗口對象(contentWindow);
很多時候我們想要繞過同源策略都得經過iframe對象其他窗口對象
這個方法實現前提:這兩個域名必須屬于同一個基礎域名,即主域相同,子域可以不同;
原理document.domain 默認的值是整個域名,所以即使兩個域名的二級域名一樣,那么他們的 document.domain 也不一樣。通過把兩個頁面的document.domain都設置成相同域名(只能設置成自身或者更高一級的父域,且主域必須相同),兩個頁面之間可以獲取window對象,和下圖部分屬性和方法;
二級域名SLD(Second-level domain):是互聯網DNS等級之中,處于頂級域名之下的域。二級域名是域名的倒數第二個部分,二級域名就是主域名分出來的域名。
二級域名是寄存在主域名之下的域名。
二級域名屬于一個獨立的分支,他有自己的收錄、快照、PR值、反鏈等。
當主域名受到懲罰,二級域名也會連帶懲罰。
以此為例,我們有域名劃分為:
.com 頂級域名example.com
example.com 一級域名
A.example.com 二級域名
B.example.com 二級域名
A.example.com
document.domain = "example.com";
一般使用不建議直接頁面插入iframe,所以我們可以自己封裝方法使用
function createIframe(url, id, callback) { var iframe = document.createElement("iframe"); iframe.id = id || "iframe"; iframe.src = url; iframe.style.display = "none"; iframe.onload = function() { //this指向 callback && callback.call(iframe); }; document.body.appendChild(iframe); }
也可以在服務器通過設置Set-Cookie,讓客戶端下擁有相同父域下所有子域名共享cookie;
window.name 原理在一個窗口 (window) 的生命周期內,窗口載入的所有的頁面都是共享一個 window.name 的,每個頁面對 window.name 都有讀寫的權限,window.name 是持久存在一個窗口載入過的所有頁面中的,并不會因新頁面的載入而進行重置。換成人話就是在同一個窗口打開的所有頁面即使不同域名不同頁面它們也會共享同一個name值(包括跳轉,iframe等都屬于并且可以支持非常長的 name 值2MB視瀏覽器而定)
優點與 document.domain 方法相比,放寬了域名后綴要相同的限制,可以從任意頁面獲取 string 類型的數據。
例如下面會跳到百度,然后進入打印window.name依然能拿到設置的字符串
window.name = "123"; window.location.;
上面的例子應該能起到一個拋磚引玉的作用了,接下來說所怎么通過window.name進行跨域;
條件,分別需要三個頁面http://www.A.com/index.html,http://www.B.com/data.php,http://www.A.com/data.html,注意是否同源;
index.html通過動態創建一個iframe作為中間的橋梁,然后src屬性指向遠端服務器data.php發起請求;
data.php設置name,因為不能跨域操作name值,所以只能再次重定向回同源頁面data.html;
同源頁面data.html可以直接獲取數據,index.html也可以無障礙操作iframe,拿到name值后立馬移除iframe保證安全;
http://www.A.com/index.htmlfunction createIframe(url, id, callback) { var iframe = document.createElement("iframe"); iframe.id = id || "iframe"; iframe.status = true; iframe.src = url; iframe.style.display = "none"; iframe.onload = function() { //this指向 callback && callback.call(iframe); }; document.body.appendChild(iframe); } createIframe("http://www.B.com/data.php", null, function() { //防止一直重定向 if (this.status) { //跨域頁面都沒有讀寫權限,需要跳回同源頁面操作 iframe.status = false; this.contentWindow.location = "http://www.A.com/data.html"; } else { alert(JSON.parse(this.contentWindow.name)); //釋放內存,移除iframe this.contentWindow.document.write(""); this.contentWindow.close(); document.body.removeChild(this); } });http://www.B.com/data.html
window.name = "{"name":"data"}"; " ?>location.hash
這個方法實現前提有兩個:
利用url改變hash(URL 的錨部分,從 # 號開始的部分)并不會導致頁面刷新;
HTML 5新增的事件onhashchange,當#值發生變化時,就會觸發這個事件;
優點:HTTP請求過程中不會攜帶hash,所以這部分的修改不會產生HTTP請求,只有轉碼瀏覽器才會將其作為實義字符處理;
#后面出現的任何字符,都會被瀏覽器解讀為位置標識符,瀏覽器只會滾動到相應位置,不會重新加載網頁。
缺點:每次修改都會產生瀏覽器歷史記錄;
有些瀏覽器不支持onhashchange事件,需要輪詢來獲知URL的改變;
數據直接暴露在了url中
過程繁瑣,起碼我認為是
思路:條件,分別需要三個頁面http://www.A.com/A.html,http://www.B.com/B.html,http://www.A.com/C.html,注意是否同源;
A.html下創建iframe打開B.html,聲明回調方法備用,并且修改hash;
B.html下創建iframe打開C.html, 監聽onhashchange事件改變hash;
C.html下監聽onhashchange事件,因為跟A.html同源可以往上層追蹤觸發到A.html的回調方法
http://www.A.com/A.htmlfunction createIframe(url, id, callback) { var iframe = document.createElement("iframe"); iframe.id = id || "iframe"; iframe.src = url; iframe.style.display = "none"; iframe.onload = function() { //this指向 callback && callback.call(iframe); }; document.body.appendChild(iframe); } createIframe("http://www.B.com/B.html", null, function() { var self = this; setTimeout(function() { self.src = self.src + "#name=123"; }, 1000); }); window.onCallback = function(res) { console.log("I got it!!", res); };http://www.B.com/B.html
function createIframe(url, id, callback) { var iframe = document.createElement("iframe"); iframe.id = id || "iframe"; iframe.src = url; iframe.style.display = "none"; iframe.onload = function() { //this指向 callback && callback.call(iframe); }; document.body.appendChild(iframe); } createIframe("http://www.A.com/C.html"); var iframe = document.getElementById("iframe"); window.onhashchange = function() { iframe.src = iframe.src + location.hash; };http://www.A.com/C.html
window.onhashchange = function() { window.parent.parent.onCallback( "hello: " + location.hash.replace("#name=", "") ); };window.postMessage
postMessage是HTML5 XMLHttpRequest Level 2中的API,可以安全地實現跨源通信。
優點:基本支持主流瀏覽器和IE8+,支持任意基本類型或可復制的對象;
真正的跨域方法,包括協議和端口不同都可以實現;
缺點:一般來說參數 message 被使用結構化克隆算法進行序列化。這意味著您可以將各種各樣的數據對象安全地傳遞到目標窗口,而不必自己序列化它們,但部分瀏覽器只支持字符串,所以傳參非字符串最好用JSON.stringify()序列化。
先看看用法
otherWindow.postMessage(message, targetOrigin, [transfer]);
參數 | 描述 |
---|---|
otherWindow | 其他窗口的一個引用,比如iframe的contentWindow屬性、執行window.open返回的窗口對象、或者是命名過或數值索引的window.frames |
message | 將要發送到其他 window的數據。它將會被結構化克隆算法序列化。這意味著你可以不受什么限制的將數據對象安全的傳送給目標窗口而無需自己序列化 |
targetOrigin | 通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。在發送消息的時候,如果目標窗口的協議、主機地址或端口這三者的任意一項不匹配targetOrigin提供的值,那么消息就不會被發送;只有三者完全匹配,消息才會被發送。 |
transfer(可選) | 是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權將被轉移給消息的接收方,而發送一方將不再保有所有權 |
function createIframe(url, id, callback) { var iframe = document.createElement("iframe"); iframe.id = id || "iframe"; iframe.src = url; iframe.style.display = "none"; iframe.onload = function() { //this指向 callback && callback.call(iframe); }; document.body.appendChild(iframe); } createIframe("http://www.B.com/data.html", null, function() { var data = { name: "123", }; this.contentWindow.postMessage(JSON.stringify(data), "http://www.B.com"); }); window.addEventListener( "message", function(e) { console.log(e.data); }, false );http://www.B.com/data.html
window.addEventListener( "message", function(e) { console.log(JSON.parse(e.data)); window.parent.postMessage("I got it!", "http://www.A.com/"); }, false );
在這里出于本地調試原因,所以沒有驗證過信息,實際使用中務必使用origin和source屬性驗證發件人的身份,targetOrigin設置可信任的網站。
有興趣的可以在本地打開兩個頁面通信,targetOrigin設為*就可以了,然后得到下圖的對象
這個方法實現前提有兩個:
還記得上面提過的同源策略只對網頁的HTML文檔做了限制,對加載的其他靜態資源如javascript、css、圖片等仍然認為屬于同源。
JSON的誕生:
指的是 JavaScript 對象表示法(JavaScript Object Notation)
是輕量級的文本數據交換格式
獨立于語言:JSON 使用 Javascript語法來描述數據對象,但是 JSON 仍然獨立于語言和平臺。JSON 解析器和 JSON 庫支持許多不同的編程語言。 目前非常多的動態(PHP,JSP,.NET)編程語言都支持JSON。
具有自我描述性,更易理解
優點:因為script標簽引入的文件內容是不能夠被客戶端的js獲取到的(實際上凡是擁有"src"這個屬性的標簽都擁有跨域的能力),不會影響到被引用文件的安全,所以通過script標簽引入的js是不受同源策略的限制的。而通過ajax加載的文件內容是能夠被客戶端js獲取到的,所以ajax必須遵循同源策略,否則被引入文件的內容會泄漏或者存在其他風險;
它的兼容性更好,在更加古老的瀏覽器中都可以運行,不需要XMLHttpRequest或ActiveX的支持;
JSON的純字符數據格式可以簡潔的描述復雜數據,易于處理這種格式的數據;
不需要多余的中轉操作;
缺點:一旦開始沒法取消停止,沒法重新開始,也沒法捕捉錯誤;
JSONP是一種腳本注入(Script Injection)行為,所以有一定的安全隱患;
只能用GET請求;
思路:在客戶端先聲明回調函數例如callbackName,然后插入一個script標簽指向請求地址同時傳入回調函數名字進行跨域請求數據;
服務端接收到之后生成json數據,然后以入參的方式放置到一個函數名為callbackName的function,再把這段js文檔返回給客戶端;
瀏覽器解析script標簽,并執行返回的javascript文檔,此時數據作為參數,執行客戶端預先聲明好的回調函數;
//必須聲明在請求之前 function callbacnName(res) { //doSometing } function createScript(url, callbackName) { var script = document.create("script"); script.type = "text/javascript"; script.src = url + "?callback=" + callbackName; document.head.appendChild(script); } createScript("http://www.A.com/xxx.php", callbacnName);
整個過程就像前端發個信息給后臺說麻煩返回一個這種格式的js代碼給我,然后后臺返回瀏覽器解析執行了這段代碼
callbackName({ xx: "xxx", });
在jQuery里用法如下
//實際請求的url地址會變成http://www.A.com/xxx.php?callback=callbackName $.ajax({ url: "http://www.A.com/xxx.php", type: "GET", dataType: "jsonp", jsonp: "callback", //傳遞給請求處理程序或頁面的,用以獲得jsonp回調函數名的參數名(默認為:callback) jsonpCallback: "callbackName", //自定義的jsonp回調函數名稱,默認為jQuery自動生成的隨機函數名 success: function(res) { //doSometing }, });CORS(Cross-Origin Resource Sharing)跨域資源共享 原理:
跨域資源共享標準新增了一組 HTTP 首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。另外,規范要求,對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)
優點:CORS支持所有類型的HTTP請求;
CORS使用普通的XMLHttpRequest發起請求和獲得數據,可以捕捉到錯誤處理;
所有瀏覽器都支持CORS,IE瀏覽器不得低于10(IE8/9需要使用XDomainRequest對象來支持CORS);
因為瀏覽器基本支持,前端代碼差別不大,主要實現細節都在瀏覽器和服務器,想要了解更多細節,請看
阮一峰老師的跨域資源共享 CORS 詳解
網上參考的利用CORS實現跨域請求
很詳細的文章HTTP訪問控制(CORS)
因為第二篇文章的實例寫的比我好多了,主要是做好兼容跟容錯,我就放出他的代碼好了,請允許我偷個懶
function createCORSRequest(method, url) { var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { // 針對Chrome/Safari/Firefox. xhr.open(method, url, true); } else if (typeof XDomainRequest != "undefined") { // 針對IE xhr = new XDomainRequest(); xhr.open(method, url); } else { // 不支持CORS xhr = null; } return xhr; } // 輔助函數,用于解析返回的內容 function getTitle(text) { return text.match("")[1]; } // 發送CORS請求 function makeCorsRequest() { // bibliographica.org是支持CORS的 var url = "http://bibliographica.org/"; var xhr = createCORSRequest("GET", url); if (!xhr) { alert("CORS not supported"); return; } // 回應處理 xhr.onload = function() { var text = xhr.responseText; var title = getTitle(text); alert("Response from CORS request to " + url + ": " + title); }; xhr.onerror = function() { alert("Woops, there was an error making the request."); }; xhr.send(); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106393.html
摘要:今天這篇文章,我們會介紹幾種常見的方法和其中存在的問題,并提出如何基于請求攔截,快速解決跨域和代理問題的方案。因為沒有修改該請求,只是延遲發送,這樣就保持了原請求與業務服務器之間的所有鑒權等相關信息,由此解決了跨域訪問無法攜帶的問題。 近幾年,隨著 Web 開發逐漸成熟,前后端分離的架構設計越來越被眾多開發者認可,使得前端和后端可以專注各自的職能,降低溝通成本,提高開發效率。 在前后端...
一、項目背景前端項目開發一個模塊,上線前需要灰度一部分用戶,實現一個臨時的灰度方案。現有項目狀況:一個前端項目1.0.0版本后端服務1.0.0版本后端灰度服務2.0.0版本一個域名解析到前端服務80、443端口前端通過nginx轉發靜態文件1、實現原理1、打包一份前端項目2.0.0版本,上傳服務器,部署不同端口2、通過nginx獲取文件中攜帶的請求頭remote_user,在nginx代理靜態文件...
同源策略:同源策略/SOP(Sameoriginpolicy)是一類承諾,由Netscape公司1995年引進電腦瀏覽器,這是電腦瀏覽器最關鍵也最基本安全配置,如今全部適用JavaScript瀏覽器都是會使用這種對策。假如缺乏了同源策略,電腦瀏覽器很容易受XSS、CSFR等進攻。 同宗就是指"協議書+網站域名+服務器端口"三個同樣,就算兩種不同的域名跳轉相同ip詳細地址,...
你們是否想過如何優化訪問路徑里的/#/,看起來有簡單又美觀,現在我們一起看看實現。現在就為大家展示解決方法。 正常解決步驟 1. 設置路由mode 先說下router的默認mode為hash模式,有關于hash模式介紹如下: hash并不能作為傳遞,也無法將URL發送到服務器中,因此,無法到服務器中進行處理。而且它對于SEO優化也有不好的影響。我們可以換換想法,用可以使用 HTML5 ...
閱讀 2814·2021-11-18 10:02
閱讀 3673·2021-11-15 17:59
閱讀 2306·2021-09-06 15:00
閱讀 3343·2019-08-29 16:58
閱讀 1056·2019-08-26 10:34
閱讀 1581·2019-08-26 10:15
閱讀 1285·2019-08-26 10:11
閱讀 2713·2019-08-23 18:33