跨域方式及其實現

產生緣由

爲何會產生跨域呢,由於瀏覽器爲了安全採用了一系列的安全機制,其中有一個是同源策略。何爲同源策略(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

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來跨子域

跨域原理:兩個網頁一級域名相同,只是二級域名不一樣,瀏覽器容許經過設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.name + iframe

跨域原理:即在一個窗口(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頁面中,結果爲

image.png

跨域栗子🌰

好比:有一個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 + iframe

跨域原理:可使用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

跨域原理:使用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>

跨域資源共享(CORS)

跨域原理:一種跨域訪問的機制,可讓AJAX實現跨域訪問;CORS容許一個域上的網絡應用向另外一個域提交跨域AJAX請求。

注意⚠️:

  • 只有服務器設置Access-Control-Allow-Origin HTTP響應頭以後,瀏覽器將會容許跨域請求。就是使用自定義的HTTP頭部讓瀏覽器與服務器進行溝通,從而決定請求或響應是應該成功,仍是應該失敗。
  • CORS機制是同源策略出讓的安全性之一。其他兩個爲容許嵌入第三方資源和跨文檔消息機制(window.postmessage)。詳情:https://www.yuque.com/suihangadam/liulanqi/pog4pf

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();
}

Web sockets

跨域原理: 是一種瀏覽器的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反向代理將對真實服務器的請求轉移到本機服務器來避免瀏覽器的"同源策略限制"。

Nginx是一個高性能的HTTP和反向代理web服務器

image.png

注意⚠️: 反向代理隱藏了真實的服務器

擴展:正向代理隱藏了真實的客戶端。

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配置代理

前提條件

一、須要使用本地開發插件: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

http://www.javashuo.com/article/p-bhtgefww-nq.html

http://www.javashuo.com/article/p-bhtgefww-nq.html

相關文章
相關標籤/搜索