跨域的幾種常見的解決方式
今天給大家分享一下,修真院官網JSS-5任務中可能會使用到的知識點:
1.背景介紹
1.1什麼是跨域?
跨域是指一個域下的文件或指令碼試圖去請求另一個域下的資源,這裡跨域是廣義的。
廣義的跨域:
1.) 資源跳轉: A連結、重定向、表單提交
2.) 資源嵌入: link script img frame等dom標籤,還有樣式中background:url()、@font-face()等檔案外鏈
3.) 指令碼請求: js發起的ajax請求、dom和js物件的跨域操作等
在前端部分其實我們通常所說的跨域是狹義的,是由瀏覽器同源策略限制的一類請求場景。
1.2那麼是什麼同源策略呢?
同源策略/SOP(Same origin policy)是一種約定,由Netscape公司1995年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到XSS、CSFR等攻擊。所謂同源是指"協議+域名+埠"三者相同,即便兩個不同的域名指向同一個ip地址,也非同源。
同源策略限制以下幾種行為: 1.) Cookie、LocalStorage 和 IndexDB 無法讀取 2.) DOM 和 Js物件無法獲得 3.) AJAX 請求不能傳送
1.3常見的跨域場景
2.知識剖析
2.1 常見的跨域方式
JSONP
在使用XMLHTTPRequest物件傳送HTTP請求時,會遇到同源策略問題,域不同請求會被瀏覽器攔截。這時就可以選擇繞過,或者說是不使用XMLHTTPRequest物件進行傳送跨域HTTP請求。 在平常寫html時會發現比如
<script src="http://www.a.com/script/1.js"></script> <img src="http://www.b.com/1.jpg"> <link href="http://www.c.com/1.css">
這種標籤是不會遇到'跨域'問題的,嚴格上來說,這不是跨域,跨域是指在指令碼程式碼中向非同源域傳送HTTP請求,這只是跨站資源請求
那麼我們可以試試看用這種跨站資源請求的方式來實現跨域HTTP請求
NGINX的反向代理<!-- HTML表頭部分--> ... <!-- javaScript片段1--> // 如果jsonp 的請求為GET if ( ctx.method === 'GET' && ctx.url.split('?')[0] === '/getData') { // 獲取jsonp的callback let callbackName = ctx.query.callback || 'callback' let returnData = { success: true, data: { text: 'this is a jsonp api', time: new Date().getTime(), } } // jsonp的script字串 let jsonpStr = `;${callbackName}(${JSON.stringify(returnData)})` // 用text/javascript,讓請求支援跨域獲取 ctx.type = 'text/javascript' // 輸出jsonp字串 ctx.body = jsonpStr } else { ctx.body = 'hello jsonp' } })
nginx支援配置反向代理,通過反向代理實現網站的負載均衡。這部分先寫一個nginx的配置,後續需要深入研究nginx的代理模組和負載均衡模組。 nginx通過proxy_pass_http 配置代理站點,upstream實現負載均衡。
WINDOW.NAME + IFRAME
WINDOW.NAME 傳輸技術的基本原理:
當在瀏覽器中開啟一個頁面,或者在頁面中新增一個iframe時即會建立一個對應的window物件,當頁面載入另一個新的頁面時,window的name屬性是不會變的。這樣就可以利用在頁面動態新增一個iframe然後src載入資料頁面,在資料頁面將需要的資料賦值給window.name。然而此時承載iframe的parent頁面還是不能直接訪問,不在同一域下iframe的name屬性,這時只需要將iframe再載入一個與承載頁面同域的空白頁面,即可對window.name進行資料讀取
name 在瀏覽器環境中是一個全域性/window物件的屬性,且當在 frame 中載入新頁面時,name 的屬性值依舊保持不變。通過在 iframe 中載入一個資源,該目標頁面將設定 frame 的 name 屬性。此 name 屬性值可被獲取到,以訪問 Web 服務傳送的資訊。但 name 屬性僅對相同域名的 frame 可訪問。這意味著為了訪問 name 屬性,當遠端 Web 服務頁面被載入後,必須導航 frame 回到原始域。同源策略依舊防止其他 frame 訪問 name 屬性。一旦 name 屬性獲得,銷燬 frame 。
在最頂層,name 屬性是不安全的,對於所有後續頁面,設定在 name 屬性中的任何資訊都是可獲得的。然而 windowName 模組總是在一個 iframe 中載入資源,並且一旦獲取到資料,或者當你在最頂層瀏覽了一個新頁面,這個 iframe 將被銷燬,所以其他頁面永遠訪問不到 window.name 屬性
<script type="text/javascript">
iframe = document.createElement('iframe');
iframe.style.display = 'none';
var state = 0;
iframe.onload = function() {
if(state === 1) {
var data = JSON.parse(iframe.contentWindow.name);
console.log(data);
iframe.contentWindow.document.write('');
iframe.contentWindow.close();
document.body.removeChild(iframe);
} else if(state === 0) {
state = 1;
iframe.contentWindow.location = 'http://localhost:81/cross-domain/proxy.html';//指向自己根目錄下的空檔案
}
};
iframe.src = 'http://localhost:8080/data.php';//要請求的不同源資料
document.body.appendChild(iframe);
</script>
也可以node.js或者其他服務端設定cors表頭來授權跨域
3.常見問題
4.解決方案
5.編碼實戰
6.拓展思考
6.1正向代理
正向代理類似一個跳板機,代理訪問外部資源。
我是一個使用者,我訪問不了某網站,但是我能訪問一個代理伺服器,這個代理伺服器呢,他能訪問那個我不能訪問的網站,於是我先連上代理伺服器,告訴他我需要那個無法訪問網站的內容,代理伺服器去取回來,然後返回給我。從網站的角度,只在代理伺服器來取內容的時候有一次記錄,有時候並不知道是使用者的請求,也隱藏了使用者的資料,這取決於代理告不告訴網站。
客戶端必須設定正向代理伺服器,當然前提是要知道正向代理伺服器的IP地址,還有代理程式的埠。
總結來說:正向代理 是一個位於客戶端和原始伺服器(origin server)之間的伺服器,為了從原始伺服器取得內容,客戶端向代理髮送一個請求並指定目標(原始伺服器),然後代理向原始伺服器轉交請求並將獲得的內容返回給客戶端。客戶端必須要進行一些特別的設定才能使用正向代理。
正向代理的用途:
(1)訪問原來無法訪問的資源,如google
(2) 可以做快取,加速訪問資源
(3)對客戶端訪問授權,上網進行認證
(4)代理可以記錄使用者訪問記錄(上網行為管理),對外隱藏使用者資訊
6.2反向代理
客戶端是無感知代理的存在的,反向代理對外都是透明的,訪問者者並不知道自己訪問的是一個代理。因為客戶端不需要任何配置就可以訪問。
反向代理(Reverse Proxy)實際執行方式是指以代理伺服器來接受internet上的連線請求,然後將請求轉發給內部網路上的伺服器,並將從伺服器上得到的結果返回給internet上請求連線的客戶端,此時代理伺服器對外就表現為一個伺服器。
反向代理的作用:
(1)保證內網的安全,可以使用反向代理提供WAF功能,阻止web攻擊
大型網站,通常將反向代理作為公網訪問地址,Web伺服器是內網。
(2)負載均衡,通過反向代理伺服器來優化網站的負載
6.3二者的區別
借某乎上一個靈魂畫手的圖片解釋下
7.參考文獻
前端常見跨域解決方案:https://www.cnblogs.com/roam/p/7520433.html
js中幾種使用的跨域方法詳解:https://www.cnblogs.com/2050/p/3191744.html
8.更多討論
一、哪種跨域常用?
淘寶、百度、網易雲音樂,之類會配置公共的API,會是JSONP
公司內的話會選擇window.name或者nginx反向代理
我們任務中會使用nginx.conf進行反向代理
二、從安全性而言選擇那種跨域方式最好,為什麼?
一般最安全的是WINDOW.NAME,因為iframe會銷燬三、JSONP的缺點
jsonp有個缺陷就是隻能get
而且會把請求的內容傳送到url中導致安全性極低