前端常見(jiàn)跨域方案匯總

為什么會(huì)存在同源策略

概念

1995年有Netscape公司引入的一個(gè)安全策略,現(xiàn)在所有瀏覽器都在使用這個(gè)策略。它限制了一個(gè)源從另外一個(gè)源請(qǐng)求資源,用于隔離潛在惡意文件伐割。

什么才是不同的源

我們都知道一般的地址又下面三部分組成

  • 協(xié)議相同
  • 域名相同
  • 端口相同

只要兩個(gè)地址其中有一個(gè)不相同候味,那么這兩個(gè)地址就是不同的源。

舉個(gè)例子

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

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

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:不同源(端口不同) 

同源的目的

目的是為了保護(hù)用戶信息的安全隔心,防止惡意網(wǎng)站竊取數(shù)據(jù)白群,否則Cookie可以共享。有的網(wǎng)站一般會(huì)把一些重要信息存放在cookie或者LocalStorage中硬霍,這時(shí)如果別的網(wǎng)站能夠獲取獲取到這個(gè)數(shù)據(jù)帜慢,可想而知,這樣就沒(méi)有什么安全可言了唯卖。

限制范圍

目前共有三種行為受到限制

  • Cookie粱玲、LocalStorage和IndexDB 無(wú)法讀取
  • DOM無(wú)法獲得
  • AJAX 請(qǐng)求不能發(fā)送

解決方案

1、通過(guò)jsonp跨域

原理

script拜轨、img抽减、iframe等標(biāo)簽的src屬性都擁有跨域請(qǐng)求資源的能力,我們可以把js撩轰、css胯甩、img等資源放到一個(gè)獨(dú)立域名的服務(wù)器上昧廷。然后通過(guò)動(dòng)態(tài)創(chuàng)建script標(biāo)簽堪嫂,再請(qǐng)求一個(gè)回調(diào)函數(shù)的網(wǎng)址,服務(wù)器把需要傳遞的參數(shù)塞入這個(gè)回調(diào)函數(shù)中木柬, 然后我們?cè)趈s代碼中執(zhí)行這個(gè)函數(shù)就可已獲取到你想要的參數(shù)皆串。

前端原生實(shí)現(xiàn)

<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實(shí)現(xiàn)

   $.ajax({
             type: "get",
             url: "https://www.address.com:433/json",
             dataType: "jsonp",
             jsonp: "callback",
             jsonpCallback:"xxx"http://自定義回調(diào)函數(shù)名
             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 服務(wù)端

//需要看egg官網(wǎng)構(gòu)建一個(gè)simple框架
egg-init --type=simple

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

缺點(diǎn)

  • 只支持GET請(qǐng)求,不支持POST請(qǐng)求
  • 調(diào)用失敗沒(méi)有HTTP狀態(tài)碼
  • 不夠安全眉枕。
2恶复、CORS

原理

CORS(Cross-Origin Resource Sharing)跨資源分享,瀏覽器不能低于IE10,服務(wù)器支持任何類型的請(qǐng)求速挑。

熟悉幾個(gè)后臺(tái)需要設(shè)置的字段

  • Access-Control-Allow-Origin

字段必傳谤牡,為*表示允許任意域名的請(qǐng)求。當(dāng)有cookie需要傳遞時(shí)姥宝,需要指定域名翅萤。

  • Access-Control-Allow-Credentials

字段可選,默認(rèn)為false,表示是否允許發(fā)送cookie腊满。若允許套么,通知瀏覽器也要開(kāi)啟cookie值的傳遞。

  • Access-Control-Expose-Headers

字段可選碳蛋。如果想要瀏覽器拿到getResponesHeader()其他字段胚泌,就在這里指定。

  • Access-Control-Request-Method

必須字段肃弟,非簡(jiǎn)單請(qǐng)求時(shí)設(shè)置的字段玷室,例如PUT請(qǐng)求零蓉。

  • Access-Control-Request-Headers

指定額外的發(fā)送頭信息,以逗號(hào)分割字符串穷缤。

前端原生代碼

var xhr = new XMLHttpRequest()
// 設(shè)置攜帶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
}

優(yōu)點(diǎn)

  • 支持所有類型的HTTP請(qǐng)求壁公。

缺點(diǎn)

  • 不兼容IE10以下。
3绅项、iframe結(jié)合locaction.hash方法

原理

也是利用iframe可以在不同域中傳值的特點(diǎn)紊册,而location.hash正好可以攜帶參數(shù),所以利用iframe作為這個(gè)不同域之間的橋梁快耿。

具體實(shí)現(xiàn)步驟

  • 1囊陡、向A域名頁(yè)面插入一個(gè)B域名的iframe標(biāo)簽。
  • 2掀亥、然后在B域名的iframe頁(yè)面中ajax請(qǐng)求同域名的服務(wù)器撞反。
  • 3、iframe頁(yè)面拿到數(shù)據(jù)后通過(guò)praent.location.href將想要傳遞的參數(shù)放到#后面搪花,作為hash傳遞遏片。
  • 4、這樣在A域名頁(yè)面就能通過(guò)window.onhashchange 監(jiān)聽(tīng)處理你想要的數(shù)據(jù)撮竿。

A域名頁(yè)面

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域名頁(yè)面


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)

缺點(diǎn)

  • iframe雖然能解決問(wèn)題吮便,但是安全風(fēng)險(xiǎn)還是比較重要的。
  • hash傳參處理起來(lái)比較麻煩幢踏。
4髓需、iframe結(jié)合window.name方法

原理

原理其實(shí)是和上面的方法一樣,區(qū)別在于window.name能夠傳遞2MB以上的數(shù)據(jù)房蝉。

A域名頁(yè)面

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
    }
}


// 獲取數(shù)據(jù)以后銷毀這個(gè)iframe僚匆,釋放內(nèi)存;
function destoryFrame() {
    document.body.removeChild(iframe);
}

B域名頁(yè)面

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

原理

postMessage是H5原生API支持搭幻,可以在兩個(gè)頁(yè)面或者多個(gè)頁(yè)面咧擂,以及不同源頁(yè)面之間傳遞數(shù)據(jù)。窗口之間能夠傳遞數(shù)據(jù)的前提是必須從一個(gè)窗口獲取到另外一個(gè)窗口的目標(biāo)對(duì)象(target window),比如說(shuō)用iframe打開(kāi)另外一個(gè)窗口檀蹋,我們得獲取到這個(gè)iframe的contentWindow松申。或者通過(guò)window.open()打開(kāi)另外一個(gè)窗口時(shí)會(huì)返回這個(gè)窗口的window對(duì)象续扔。

參數(shù)傳遞

/**
  data  需要傳遞的數(shù)據(jù)攻臀,使用JSON.stringify 序列化
  origin 設(shè)置為'*'時(shí),表示傳遞給所有窗口纱昧,也可以指定地址刨啸。 如果是同源下設(shè)置為'/'
**/
postMessage(data,origin)

http://localhost:8069 窗口

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

  /// 等待新窗口加載完
  setTimeout(function() {
      // 當(dāng)前窗口向目標(biāo)源傳數(shù)據(jù)
    popup.postMessage({"age":10}, 'http://localhost:8080');
  }, 1000);

http://localhost:8080 窗口

  // 設(shè)置監(jiān)聽(tīng),如果有數(shù)據(jù)傳過(guò)來(lái)识脆,則打印
  window.addEventListener('message', function(e) {
    console.log(e);
//判斷是不是目標(biāo)地址
  if(e.origin !== 'http://localhost:8069')return;
    // console.log(e.source === window.opener);  // true
//發(fā)回?cái)?shù)據(jù)
 e.source.postMessage({"age":20}, e.origin);
  });
其他的解決方式

以上五種是比較常見(jiàn)的跨域解決方案设联,各有優(yōu)缺點(diǎn)善已。沒(méi)有絕對(duì)的最優(yōu)方案,只有最合適應(yīng)用場(chǎng)景离例。

常見(jiàn)的兩種代理跨域

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

配置就不講了换团,其實(shí)我也不熟悉,平時(shí)工作用不到這么高大上的宫蛆。就來(lái)講講原理以及思路艘包。

什么是代理

既然是代理跨域,那么代理(Proxy Server)就是一個(gè)很重要的點(diǎn)耀盗,這里的代理說(shuō)的服務(wù)器代理想虎,是一種很重要的服務(wù)器安全功能,也是一種很常見(jiàn)的設(shè)計(jì)模式叛拷,來(lái)隔絕不同的模塊舌厨,解耦模塊。生活中也很容易見(jiàn)到這種模式忿薇,我們買房(買不起呀)買車裙椭,就好比跟一個(gè)大型服務(wù)器打交道,別人是大老板署浩,自然不會(huì)那么容易親自接待你揉燃,這時(shí)候有中介在中間,幫你們兩方理清好思路瑰抵,做好鋪墊你雌,然后后面的交流才會(huì)更加順通高效器联。我們?yōu)g覽器訪問(wèn)不同源的服務(wù)器是有限定的二汛,但是nginx和node中間件這些代理就沒(méi)有跨域的限定,所以我們可以放心的把任務(wù)交給他們拨拓,讓他們幫我們?nèi)プ鑫覀冏霾涣说氖隆?/p>

為什么代理是反的

我們知道單個(gè)服務(wù)器的處理能力是有限的肴颊,就好比如中國(guó)也不可能只有一家汽車生產(chǎn)商,中國(guó)那么大的市場(chǎng)渣磷,自然需要很多生產(chǎn)商才能滿足的過(guò)來(lái)婿着。那么用戶選購(gòu)的時(shí)候需要節(jié)省時(shí)間和精力,汽車之間就承擔(dān)這樣一個(gè)角色醋界,把成千上萬(wàn)的用戶需求分配出去竟宋,nginx就能夠把用戶的請(qǐng)求分發(fā)到空閑的服務(wù)器上,然后服務(wù)器返回自己的服務(wù)到負(fù)載均衡設(shè)備上形纺,然后負(fù)載均衡的設(shè)備會(huì)講服務(wù)器的服務(wù)返回給用戶丘侠,所以我們并不知道為什么服務(wù)的是哪一臺(tái)服務(wù)器發(fā)送出來(lái)的,這就很好的隱藏了服務(wù)器逐样。有一句精辟的話是這么說(shuō)的:“反向代理就是流量發(fā)散狀蜗字,代理是流量匯聚狀打肝。”

最后附上一張畫(huà)的很丑陋的圖

反向代理.png

如果大神您想繼續(xù)探討或者學(xué)習(xí)更多知識(shí)挪捕,歡迎加入QQ或者微信一起探討:854280588
QQ.png
微信
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末粗梭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子级零,更是在濱河造成了極大的恐慌断医,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件奏纪,死亡現(xiàn)場(chǎng)離奇詭異孩锡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)亥贸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門躬窜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人炕置,你說(shuō)我怎么就攤上這事荣挨。” “怎么了朴摊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵默垄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我甚纲,道長(zhǎng)口锭,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任介杆,我火速辦了婚禮鹃操,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘春哨。我一直安慰自己荆隘,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開(kāi)白布赴背。 她就那樣靜靜地躺著椰拒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凰荚。 梳的紋絲不亂的頭發(fā)上燃观,一...
    開(kāi)封第一講書(shū)人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音便瑟,去河邊找鬼缆毁。 笑死,一個(gè)胖子當(dāng)著我的面吹牛胳徽,可吹牛的內(nèi)容都是我干的积锅。 我是一名探鬼主播爽彤,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼缚陷!你這毒婦竟也來(lái)了适篙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤箫爷,失蹤者是張志新(化名)和其女友劉穎嚷节,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體虎锚,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硫痰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了窜护。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片效斑。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖柱徙,靈堂內(nèi)的尸體忽然破棺而出缓屠,到底是詐尸還是另有隱情,我是刑警寧澤护侮,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布敌完,位于F島的核電站,受9級(jí)特大地震影響羊初,放射性物質(zhì)發(fā)生泄漏滨溉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一长赞、第九天 我趴在偏房一處隱蔽的房頂上張望晦攒。 院中可真熱鬧,春花似錦涧卵、人聲如沸勤家。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至热幔,卻和暖如春乐设,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绎巨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工近尚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人场勤。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓戈锻,卻偏偏與公主長(zhǎng)得像歼跟,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子格遭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 原文地址:原文地址 什么是跨域哈街? 跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,這里跨域是廣義的拒迅。 廣義...
    C_Y大漁閱讀 1,259評(píng)論 1 13
  • 什么是跨域骚秦? 2.) 資源嵌入:、璧微、作箍、等dom標(biāo)簽,還有樣式中background:url()前硫、@font-fac...
    電影里的夢(mèng)i閱讀 2,374評(píng)論 0 5
  • 什么是跨域 跨域胞得,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的屹电,是瀏覽器對(duì)JavaScript實(shí)...
    Yaoxue9閱讀 1,301評(píng)論 0 6
  • 什么是跨域 跨域懒震,是指瀏覽器不能執(zhí)行其他網(wǎng)站的腳本。它是由瀏覽器的同源策略造成的嗤详,是瀏覽器對(duì)JavaScript實(shí)...
    他方l閱讀 1,064評(píng)論 0 2
  • 我的父母一直認(rèn)為我不會(huì)說(shuō)話,掌握不好說(shuō)話的藝術(shù)苍狰。然而我認(rèn)為我是不愿意和長(zhǎng)輩之間交談办龄,因?yàn)楦麄冎g沒(méi)什么好說(shuō)...
    妖孽LWL閱讀 472評(píng)論 0 0