爲何會產生跨域呢,由於瀏覽器爲了安全採用了一系列的安全機制,其中有一個是同源策略。何爲同源策略(same-origin policy)。簡單來說同源策略就是瀏覽器爲了保證用戶信息的安全,防止惡意的網站竊取數據,禁止不一樣域之間的JS進行交互。對於瀏覽器而言只要域名、協議、端口其中一個不一樣就會引起同源策略。javascript
關於瀏覽器安全和同源策略詳解 https://www.yuque.com/suihangadam/liulanqi/pog4pfhtml
公司內部可能有多個不一樣的子域,好比一個是location.company.com ,而應用是放在app.company.com , 這時想從 app.company.com去訪問 location.company.com 的資源就屬於跨域。html5
jsonp是一種非正式的傳輸協議java
跨域原理: 利用了src不受同源策略的影響 ,能夠訪問其餘頁面的數據。webpack
注意⚠️:jsonp並不能解決全部的跨域問題,由於使用jsonp跨域須要被提供jsonp接口nginx
// 1.建立一個全局函數 function callBack (data) { console.log(data); } // 2.動態建立一個script標籤 var currentScript = document.createElement("script"); // 3.給標籤的src賦值(即接口的url),並將函數附加到url上,注意:大部分jonsp接口都爲callback,百度的jsonp接口爲cb currentScript.src = "http:www.baidu.com?a=1&b=2&cb=callBack"; // 4.將標籤插入到頁面上 document.body.appendChild(script1); // 5.將標籤加載完後刪除 script1.onload = function(){ this.remove() }
跨域原理:兩個網頁一級域名相同,只是二級域名不一樣,瀏覽器容許經過設document.domain共享 Cookie或者處理iframe。git
注意⚠️:用來處理Cookie 和 iframe,github
處理Cookieweb
document.domain = 'example.com'; //如今,A網頁經過腳本設置一個 Cookie。 document.cookie = "test1=hello"; //B網頁就能夠讀到這個 Cookie。 var allCookie = document.cookie;
注意⚠️:這種方法只適用於 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 沒法經過這種方法,規避同源政策,而要使用下文介紹的PostMessage API。 chrome
另外,服務器也能夠在設置Cookie的時候,指定Cookie的所屬域名爲一級域名,好比.example.com。
Set-Cookie: key=value; domain=.example.com; path=/ //這樣的話,二級域名和三級域名不用作任何設置,均可以讀取這個Cookie。
處理iframe
不一樣的iframe 之間(父子或同輩),是可以獲取到彼此的window對象的,可是你卻不能使用獲取到的window對象的屬性和方法(html5中的postMessage方法是一個例外,還有些瀏覽器好比ie6也可使用top、parent等少數幾個屬性),總之,你能夠當作是隻能獲取到一個幾乎無用的window對象。
首先說明一下同域之間的iframe是能夠操做的。好比http://127.0.0.1/JSONP/a.html裏面嵌入一個iframe指向http://127.0.0.1/myPHP/b.html。那麼在a.html裏面是能夠操做iframe裏面的DOM的。
<iframe src="http://127.0.0.1/myPHP/b.html" frameborder="1"></iframe> <body> <script type="text/javascript"> var iframe = document.querySelector("iframe"); iframe.onload = function(){ var win = iframe.contentWindow; var doc = win.document; var ele = doc.querySelector(".text1"); var text = ele.innerHTML="123456"; } </script>
注意⚠️:若是兩個網頁不一樣源,就沒法拿到對方的DOM。典型的例子是iframe窗口和window.open方法打開的窗口,它們與父窗口沒法通訊。若是兩個窗口一級域名相同,只是二級域名不一樣,那麼document.domain屬性,就能夠規避同源政策,拿到DOM。
跨域原理:即在一個窗口(window)的生命週期內,窗口載入的全部的頁面都是共享一個window.name的,每一個頁面window.name都有讀寫的權限。
注意⚠️:是在一個窗口!而且window.name的值只能是字符串的形式,這個字符串的大小最大能容許2M左右甚至更大的一個容量,具體取決於不一樣的瀏覽器。
簡單栗子🌰
1.建立一個a.html
<script> window.name = "哈哈,我是頁面a設置的值喲!"; //設置window.name的值 setTimeout(function(){ window.location = 'b.html'; },3000);//3秒後把一個新頁面載入當前window </script>
2.在b.html讀取
<script> alert(window.name);//讀取window.name的值 </script>
3.a.html載入3秒後,跳轉到b.html頁面中,結果爲
跨域栗子🌰
好比:有一個example.com/a.html頁面,須要經過a.html頁面裏的js來獲取另外一個位於不一樣域上的頁面cnblogs.com/data.html裏的數據。
1.建立cnblogs.com/data.html代碼
<script> window.name = "我是data.html的數據,全部能夠轉化爲字符串來傳遞的數據均可以在這裏使用,好比這裏能夠傳遞Json數據"; </script>
2.建立example.com/a.html的代碼
想要即便a.html頁面不跳轉也能獲得data.html裏的數據。在a.html頁面中使用一個隱藏的iframe來充當一箇中間人角色,由iframe去獲取data.html的數據,而後a.html再去獲得iframe獲取到的數據。
<iframe id = "iframe" src = "cnblogs.com/data.html" style = "display:none" onload = "getData()"></iframe> <script> function getData(){ //iframe載入data.html頁面會執行此函數 var ifr = document.getElementById("iframe"); ifr.onload = function(){ //這個時候iframe和a.html已經處於同一源,能夠互相訪問 var data = ifr.contentWindow.name; //獲取iframe中的window.name,也就是data.html中給它設置的數據 alert(data); } ifr.src = 'b.html';//這裏的b.html爲隨便一個頁面,只要與a.html同源就行,目的是讓a.html可以訪問到iframe中的東西,不然訪問不到 } </script>
跨域原理:可使用window.postMessage()來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源(可實現跨域),其餘的頁面經過window.onmessage來接收。
注意⚠️:window.postMessage(message,targetOrigin) 方法是html5新引進的特性,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。但IE六、IE7不支持
message:爲要發送的消息,類型只能爲字符串;
targetOrigin:用來限定接收消息的那個window對象所在的域,若是不想限定域,可使用通配符 「*」。
栗子🌰
1.建立www.test.com/a.html頁面代碼
<iframe id="iframe" src="www.script.com/b.html" onload="onLoad()"></iframe> <script> function onLoad(){ var iframe = document.getElementById("iframe"); var win = iframe.contentWindow; win.postMessage('哈哈,我是來自頁面a.html的信息喲!','*');//向不一樣域的www.script.com/b.html發送消息 } </script>
2.建立www.script.com/b.html頁面代碼
<script> window.onmessage = function(e){ //註冊message時間來接收消息 e = e || event; //獲取時間對象 alert(e.data); //經過data屬性來獲得傳送的消息 } </script>
跨域原理:使用location.hash和iframe來繞過同源策略
注意⚠️:須要借用兩個iframe
假設域名test.com下的文件a.html要和csdnblogs.com域名下的b.html傳遞信息
1.建立test.com下的a.html頁面, 同時在a.html上加一個定時器,隔一段時間來判斷location.hash的值有沒有變化,一旦有變化則獲取獲取hash值
www.test.com下a.html代碼
<script> function startRequest(){ var ifr = document.createElement('iframe'); //建立一個隱藏的iframe ifr.style.display = 'none'; ifr.src = 'http://www.csdnblogs.com/b.html#paramdo'; //傳遞的location.hash document.body.appendChild(ifr); } function checkHash() { try { var data = location.hash ? location.hash.substring(1):''; if (console.log) { console.log('Now the data is ' + data); } } catch (e) {}; } setInterval(checkHash, 5000); window.onload = startRequest; </script>
2.b.html響應請求後再將經過修改test.com域名下的文件c.html來間接a.html的hash值來傳遞數據,代碼以下:
www.csdnblogs.com域名下b.html代碼
<script> function checkHash() { var data = ''; //模擬一個簡單的參數處理操做 switch (location.hash) { case '#paramdo': data = 'somedata'; break; case '#paramset': //do something…… break; default: break; } data && callBack('#' + data); } function callBack(hash) { // ie、chrome的安全機制沒法修改parent.location.hash //因此要利用一箇中間的www.csdnblogs.com域下的代理iframe var proxy = document.createElement('iframe'); proxy.style.display = 'none'; proxy.src = 'http://www.csdnblogs.com/c.html' + hash; // 注意該文件在"www.test.com"域下 document.body.appendChild(proxy); } window.onload = checkHash; </script>
www.test.com下c.html代碼
<script> //由於parent.parent和自身屬於同一個域,因此能夠改變其location.hash的值 parent.parent.location.hash = self.location.hash.substring(1); </script>
跨域原理:一種跨域訪問的機制,可讓AJAX實現跨域訪問;CORS容許一個域上的網絡應用向另外一個域提交跨域AJAX請求。
注意⚠️:
IE中對CORS的實現是經過xdr
var xdr = new XDomainRequest(); xdr.onload = function(){ console.log(xdr.responseText); } xdr.open('get', 'http://www.test.com'); ...... xdr.send(null);
其它瀏覽器中的實現就在xhr中
var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function () { if(xhr.readyState === 4 && xhr.status === 200){ console.log(xhr.responseText); } } } xhr.open('get', 'http://www.test.com'); ...... xhr.send(null);
實現跨瀏覽器的CORS
function createCORS(method, url){ var xhr = new XMLHttpRequest(); if('withCredentials' in xhr){ xhr.open(method, url, true); }else if(typeof XDomainRequest != 'undefined'){ var xhr = new XDomainRequest(); xhr.open(method, url); }else{ xhr = null; } return xhr; } var request = createCORS('get', 'http://www.test.com'); if(request){ request.onload = function(){ ...... }; request.send(); }
跨域原理: 是一種瀏覽器的API,它的目標是在一個單獨的持久鏈接上提供全雙工、雙向通訊。(同源策略對web sockets不適用),在JS建立了web socket以後,會有一個HTTP請求發送到瀏覽器以發起鏈接。取得服務器響應後,創建的鏈接會使用HTTP升級從HTTP協議交換爲web sockt協議。
<script> var socket = new WebSockt('ws://www.test.com'); //http->ws; https->wss socket.send('hello WebSockt'); socket.onmessage = function(event){ var data = event.data; } </script>
跨域原理:Nginx反向代理將對真實服務器的請求轉移到本機服務器來避免瀏覽器的"同源策略限制"。
Nginx是一個高性能的HTTP和反向代理web服務器
注意⚠️: 反向代理隱藏了真實的服務器
擴展:正向代理隱藏了真實的客戶端。
Nginx 反向代理模塊 proxy_pass
proxy_pass 後面跟着一個 URL,用來將請求反向代理到 URL 參數指定的服務器上。例如咱們上面例子中的 proxy_pass https://api.shanbay.com,則將匹配的請求反向代理到 https://api.shanbay.com。
經過在配置文件中增長proxy_pass 你的服務器ip,就能夠完成反向代理。
server { listen 80; server_name localhost; ## 用戶訪問 localhost,則反向代理到https://api.shanbay.com location / { root html; index index.html index.htm; proxy_pass https://api.shanbay.com; } }
nginx反向代理詳細配置:http://www.javashuo.com/article/p-auajxehm-x.html
前提條件
一、須要使用本地開發插件:webpack-dev-server。
二、webpack-dev-server使用的是http-proxy-middleware來實現跨域代理的。
module.exports = { //... devServer: { proxy: { '/api': { target: 'http://www.baidu.com/', pathRewrite: {'^/api' : ''}, changeOrigin: true, // target是域名的話,須要這個參數, secure: false, // 設置支持https協議的代理 }, '/api2': { ..... } } } };
參考自
百度百科
https://blog.csdn.net/wangchengiii/article/details/78081032