前端常見跨域方案彙總

爲何會存在同源策略

概念css

1995年有Netscape公司引入的一個安全策略,如今全部瀏覽器都在使用這個策略。它限制了一個源從另一個源請求資源,用於隔離潛在惡意文件。html

什麼纔是不一樣的源前端

咱們都知道通常的地址又下面三部分組成node

  • 協議相同
  • 域名相同
  • 端口相同

只要兩個地址其中有一個不相同,那麼這兩個地址就是不一樣的源。jquery

舉個例子nginx

//地址
http://www.address.com/item/page.html

協議: http://
域名:www.example.com
端口:8080(http)/443(https) (端口默認省略)

http://www.address.com/item2/other.html:同源

http://address.com/item/other.html:不一樣源(域名不一樣)

http://v2.www.address.com/item/other.html:不一樣源(域名不一樣)

http://www.address.com:81/item/other.html:不一樣源(端口不一樣)	
複製代碼

同源的目的ajax

目的是爲了保護用戶信息的安全,防止惡意網站竊取數據,不然Cookie能夠共享。有的網站通常會把一些重要信息存放在cookie或者LocalStorage中,這時若是別的網站可以獲取獲取到這個數據,可想而知,這樣就沒有什麼安全可言了。npm

限制範圍json

目前共有三種行爲受到限制後端

  • Cookie、LocalStorage和IndexDB 沒法讀取
  • DOM沒法得到
  • AJAX 請求不能發送

解決方案

一、經過jsonp跨域

原理

script、img、iframe等標籤的src屬性都擁有跨域請求資源的能力,咱們能夠把js、css、img等資源放到一個獨立域名的服務器上。而後經過動態建立script標籤,再請求一個回調函數的網址,服務器把須要傳遞的參數塞入這個回調函數中, 而後咱們在js代碼中執行這個函數就可已獲取到你想要的參數。

前端原生實現

<script>
	window.xxx = function (value) {
 		 console.log(value)
	}

	var script = document.createElement('script')
	script.src = 'https://www.address.com:433/json?callback=xxx'
	document.body.appendChild(script)
</script>
複製代碼

jquery實現

$.ajax({
             type: "get",
             url: "https://www.address.com:433/json",
             dataType: "jsonp",
             jsonp: "callback",
			 jsonpCallback:"xxx"//自定義回調函數名
             success: function(res){
             	console.log(res)
             },
             error: function(){
                 console.log('fail');
             }
         });
複製代碼

jsonp插件

//安裝 
 npm install jsonp

const jsonp = require('jsonp');

jsonp('https://www.address.com:433/json', {parma:'xxx'}, (err, data) => {
  if (err) {
    console.error(err.message);
  } else {
    console.log(data);
  }
});

複製代碼

node.js egg 服務端

//須要看egg官網構建一個simple框架
egg-init --type=simple

// router.js  用egg內置jspnp方法  https://eggjs.org/api/Config.html#jsonp
module.exports = app => {
  app.get('/json', app.jsonp({ callback: 'xxx' }), app.controller.json.index)
}

複製代碼

缺點

  • 只支持GET請求,不支持POST請求
  • 調用失敗沒有HTTP狀態碼
  • 不夠安全。
二、CORS

原理

CORS(Cross-Origin Resource Sharing)跨資源分享,瀏覽器不能低於IE10,服務器支持任何類型的請求。

熟悉幾個後臺須要設置的字段

  • Access-Control-Allow-Origin

字段必傳,爲*表示容許任意域名的請求。當有cookie須要傳遞時,須要指定域名。

  • Access-Control-Allow-Credentials

字段可選,默認爲false,表示是否容許發送cookie。若容許,通知瀏覽器也要開啓cookie值的傳遞。

  • Access-Control-Expose-Headers

字段可選。若是想要瀏覽器拿到getResponesHeader()其餘字段,就在這裏指定。

  • Access-Control-Request-Method

必須字段,非簡單請求時設置的字段,例如PUT請求。

  • Access-Control-Request-Headers

指定額外的發送頭信息,以逗號分割字符串。

前端原生代碼

var xhr = new XMLHttpRequest()
// 設置攜帶cookie
xhr.withCredentials = true;
xhr.open('POST', 'https://www.address.com:433/json', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xhr.send(null)

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText).msg)
  }
}
複製代碼

後端代碼

module.exports = app => {
  class CrosController extends app.Controller {
    * index(req) {
      this.ctx.set('Access-Control-Allow-Origin', 'https://www.address.com');
 	  this.ctx.set('Access-Control-Allow-Credentials', 'true')
      this.ctx.body = { msg: 'hello world' }
    }
  }
  return CrosController
}

複製代碼

優勢

  • 支持全部類型的HTTP請求。

缺點

  • 不兼容IE10如下。
三、iframe結合locaction.hash方法

原理

也是利用iframe能夠在不一樣域中傳值的特色,而location.hash正好能夠攜帶參數,因此利用iframe做爲這個不一樣域之間的橋樑。

具體實現步驟

  • 一、向A域名頁面插入一個B域名的iframe標籤。
  • 二、而後在B域名的iframe頁面中ajax請求同域名的服務器。
  • 三、iframe頁面拿到數據後經過praent.location.href將想要傳遞的參數放到#後面,做爲hash傳遞。
  • 四、這樣在A域名頁面就能經過window.onhashchange 監聽處理你想要的數據。

A域名頁面

var iframe = document.createElement('iframe')
iframe.src = 'http://www.B.com:80/hash.html'
document.body.appendChild(iframe)

window.onhashchange = function () {
  //處理hash
  console.log(location.hash)

}
複製代碼

B域名頁面

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        var res = JSON.parse(xhr.responseText)
        console.log(res.msg)
        parent.location.href = `http://www.A.com:80/a.html#msg=${res.msg}`
    }
}
xhr.open('GET', 'http://www.B.com:80/json', true)
xhr.send(null)
複製代碼

缺點

  • iframe雖然能解決問題,可是安全風險仍是比較重要的。
  • hash傳參處理起來比較麻煩。
四、iframe結合window.name方法

原理

原理實際上是和上面的方法同樣,區別在於window.name可以傳遞2MB以上的數據。

A域名頁面

var iframe = document.createElement('iframe')
iframe.src = 'http://www.B.com:80/name.html'
document.body.appendChild(iframe)
var times = 0
iframe.onload = function () {
    if (times === 1) {
        console.log(JSON.parse(iframe.contentWindow.name))
        destoryFrame()
    } else if (times === 0) {
        times = 1
    }
}


// 獲取數據之後銷燬這個iframe,釋放內存;
function destoryFrame() {
    document.body.removeChild(iframe);
}
複製代碼

B域名頁面

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        window.name = xhr.responseText
        location.href = 'http://www.A.com:80/a.html'
    }
}
xhr.open('GET', 'http://www.B.com:80/json', true)
xhr.send(null)
複製代碼
五、postMessage跨域

原理

postMessage是H5原生API支持,能夠在兩個頁面或者多個頁面,以及不一樣源頁面之間傳遞數據。窗口之間可以傳遞數據的前提是必須從一個窗口獲取到另一個窗口的目標對象(target window),好比說用iframe打開另一個窗口,咱們得獲取到這個iframe的contentWindow。或者經過window.open()打開另一個窗口時會返回這個窗口的window對象。

參數傳遞

/**
  data  須要傳遞的數據,使用JSON.stringify 序列化
  origin 設置爲'*'時,表示傳遞給全部窗口,也能夠指定地址。 若是是同源下設置爲'/'
**/
postMessage(data,origin)

複製代碼

http://localhost:8069 窗口

// 打開一個新的窗口
  var popup = window.open('http://localhost:8080');

  /// 等待新窗口加載完
  setTimeout(function() {
      // 當前窗口向目標源傳數據
    popup.postMessage({"age":10}, 'http://localhost:8080');
  }, 1000);
複製代碼

http://localhost:8080 窗口

// 設置監聽,若是有數據傳過來,則打印
  window.addEventListener('message', function(e) {
    console.log(e);
//判斷是否是目標地址
  if(e.origin !== 'http://localhost:8069')return;
    // console.log(e.source === window.opener);  // true
//發回數據
 e.source.postMessage({"age":20}, e.origin);
  });
複製代碼
其餘的解決方式

以上五種是比較常見的跨域解決方案,各有優缺點。沒有絕對的最優方案,只有最合適應用場景。

常見的兩種代理跨域

  • nginx反向代理跨域 node中間件代理

配置就不講了,其實我也不熟悉,平時工做用不到這麼高大上的。就來說講原理以及思路。

什麼是代理

既然是代理跨域,那麼代理(Proxy Server)就是一個很重要的點,這裏的代理說的服務器代理,是一種很重要的服務器安全功能,也是一種很常見的設計模式,來隔毫不同的模塊,解耦模塊。生活中也很容易見到這種模式,咱們買房(買不起呀)買車,就比如跟一個大型服務器打交道,別人是大老闆,天然不會那麼容易親自接待你,這時候有中介在中間,幫大家兩方理清好思路,作好鋪墊,而後後面的交流纔會更加順通高效。咱們瀏覽器訪問不一樣源的服務器是有限定的,可是nginx和node中間件這些代理就沒有跨域的限定,因此咱們能夠放心的把任務交給他們,讓他們幫咱們去作咱們作不了的事。

爲何代理是反的

咱們知道單個服務器的處理能力是有限的,就比如如中國也不可能只有一家汽車生產商,中國那麼大的市場,天然須要不少生產商才能知足的過來。那麼用戶選購的時候須要節省時間和精力,汽車之間就承擔這樣一個角色,把成千上萬的用戶需求分配出去,nginx就可以把用戶的請求分發到空閒的服務器上,而後服務器返回本身的服務到負載均衡設備上,而後負載均衡的設備會講服務器的服務返回給用戶,因此咱們並不知道爲何服務的是哪一臺服務器發送出來的,這就很好的隱藏了服務器。有一句精闢的話是這麼說的:「反向代理就是流量發散狀,代理是流量匯聚狀。」

最後附上一張畫的很醜陋的圖

反向代理.png

若是大神您想繼續探討或者學習更多知識,歡迎加入QQ或者微信一塊兒探討:854280588

QQ.png
微信
相關文章
相關標籤/搜索