什麼是跨域請求?javascript
當瀏覽器執行一個腳本時會檢查是否同源,只有同源的腳本纔會執行,若是不一樣源即爲跨域html
什麼是同源?前端
同源即:由Netscape提出的著名安全策略,是瀏覽器最核心、基本的安全功能,它限制了一個源(origin)中加載文本或者腳本與來自其餘源(origin)中資源的交互方式
,所謂的同源就是指協議、域名、端口相同。java
只要協議、域名、端口有任何一個不一樣,都被看成是不一樣的域,之間的請求就是跨域操做。
協議?域名?端口?web
協議:網絡協議遍佈OSI通訊模型(OSI七層模型,經常使用協議有TCP/IP、HTTP、FTP協議等)
域名:Domain Name,網域,是由一串用點分隔的名字組成的Internet上某一臺計算機或計算機組的名稱,用於在數據傳輸時標識計算機的電子方位(有時也指地理位置)
端口:是設備與外界通信交流的出口,分爲物理端口和虛擬端口(常見的如80端口)json
瞭解概念後咱們知道跨域請求就是web瀏覽器自身不容許在域名、協議、端口等都不相同的狀況下進行頁面請求方爲,所以做爲前端developer在項目開發時是須要解決此類問題的!後端
爲何要有這種限制?非同源請求頁面會怎麼樣?跨域
設想這樣一個情景:A網站是一家銀行,用戶登陸之後,又去瀏覽其餘的網站B,若是網站B能夠讀取A網站的Cookie,會發生什麼問題?
顯然,若是Cookie包含隱私(好比存款總額),這些信息就會泄露,更可怕的是,Cookie每每用來保存用戶的登陸狀態,若是用戶沒有退出登陸,其餘網站就能夠冒用戶,隨心所欲。由於瀏覽器同時還規定,提交表單不受同源策略的限制。瀏覽器
非同源限制範圍安全
跨域請求解決方案
1. jsonp:(JSON with Padding是JSON的一種「使用模式」,可用於解決主流瀏覽器的跨域數據訪問的問題)
原理:網頁客戶端動態添加<script>標籤添加src屬性,向服務端發送json請求(不受同源策略束縛)服務器收到請求後,將數據放在一個指定名字的回調函數裏(做爲參數)傳回來。
// 前端請求代碼 //http://127.0.0.1:8888/jsonp.html var script = document.createElement('script'); script.src = 'http://127.0.0.1:2333/jsonpHandler?callback=_callback' document.body.appendChild(script); //插入script標籤 //回調處理函數 _callback var _callback = function(obj){ for(key in obj){ console.log('key: ' + key +' value: ' + obj[key]); } } // 後端響應代碼 //http://127.0.0.1:2333/jsonpHandler app.get('/jsonpHandler', (req,res) => { let callback = req.query.callback; let obj = { type : 'jsonp', name : 'weapon-x' }; res.writeHead(200, {"Content-Type": "text/javascript"}); res.end(callback + '(' + JSON.stringify(obj) + ')'); })
JSONP只支持Get請求方式
2.CORS:(跨域資源共享)是由W3C制定的跨站資源分享標準,可讓AJAX實現跨域訪問,除了 GET 要求方法之外也支持其餘的 HTTP 要求。服務器通常須要增長以下響應頭的一種或幾種:
Access-Control-Allow-Origin: * Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400
跨域請求默認不會攜帶Cookie信息,若是須要攜帶,請配置下述參數:
"Access-Control-Allow-Credentials": true // Ajax設置 "withCredentials": true
3.window.name+iframe:利用iframe標籤的跨域能力,window.name屬性值在文檔刷新後依舊存在的能力(且最大容許2M左右)
<!-- 下述用端口 10000表示:domainA 10001表示:domainB --> <!-- localhost:10000 --> <script> var iframe = document.createElement('iframe'); iframe.style.display = 'none'; // 隱藏 var state = 0; // 防止頁面無限刷新 iframe.onload = function() { if(state === 1) { console.log(JSON.parse(iframe.contentWindow.name)); // 清除建立的iframe iframe.contentWindow.document.write(''); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if(state === 0) { state = 1; // 加載完成,指向當前域,防止錯誤(proxy.html爲空白頁面) // Blocked a frame with origin "http://localhost:10000" from accessing a cross-origin frame. iframe.contentWindow.location = 'http://localhost:10000/proxy.html'; } }; iframe.src = 'http://localhost:10001'; document.body.appendChild(iframe); </script> <!-- localhost:10001 --> <!DOCTYPE html> ... <script> window.name = JSON.stringify({a: 1, b: 2}); </script> </html>
4.window.name:window對象有個name屬性,該屬性有個特徵:即在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面對window.name都有讀寫的權限,window.name是持久存在一個窗口載入過的全部頁面中的,並不會因新頁面的載入而進行重置。
<script> //a頁面設置 window.name="kowalski"; </script> <script> //b頁面取出 alert(window.name); </script>
正在接觸後續補充...