【跨域】JSONP/CORS/降域/postMessage

瀏覽器的同源策略

瀏覽器出于安全方面的考慮危纫,只允許與本域下的接口交互。不同源的客戶端腳本在沒有明確授權(quán)的情況下掰派,不能讀寫對方的資源从诲。

本域指的是?

如:

http://zeeliu.com/a/b.js 和 http://zeeliu.com/index.php (同源)

不同源的例子:

http://zeeliu.com/main.js 和 https://zeeliu.com/a.php (協(xié)議不同)
http://zeeliu.com/main.js 和 http://bbs.zeeliu.com/a.php (域名不同靡羡,域名必須完全相同才可以)
http://zeeliu.com/main.js 和 http://zeeliu.com:8080/a.php (端口不同,第一個是80)

四中跨域方法

JSONP

JSONP是服務(wù)器與客戶端跨源通信的常用方法系洛。最大特點就是簡單適用俊性,老式瀏覽器全部支持,服務(wù)器改造非常小描扯。

html中script標(biāo)簽可以引入其他域下的js定页,比如引入線上的jquery庫。利用這個特性绽诚,可實現(xiàn)跨域訪問接口典徊。需要后端支持

  • 定義數(shù)據(jù)處理函數(shù):_fn
  • 創(chuàng)建script標(biāo)簽,src的地址執(zhí)行后端接口憔购,最后加個參數(shù)callback=_fun
  • 服務(wù)端在收到請求后宫峦,解析參數(shù),計算返還數(shù)據(jù)玫鸟,輸出 fun(data) 字符串导绷。
  • fun(data)會放到script標(biāo)簽做為js執(zhí)行。此時會調(diào)用fun函數(shù)屎飘,將data做為參數(shù)妥曲。

代碼如下

index.html代碼:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>jsonp跨域</title>
  <style>
    .container {
      width: 500px;
      margin: 0 auto;
    }

  </style>
</head>
<body>
  <div class="container">
    <ul class="news">
      <li>海軍首批空中女戰(zhàn)勤加入戰(zhàn)斗序列</li>
      <li>運(yùn)20完成首次人員空運(yùn)試驗:乘客均為研發(fā)團(tuán)隊成員</li>
      <li>英媒稱中國將引領(lǐng)科技變革:新四大發(fā)明走向全球</li>        
    </ul>
    <button class="change">換一組</button>
  </div>

  <script>
  
    $('.change').addEventListener('click', function(){
      //點擊按鈕后創(chuàng)建一個<script>標(biāo)簽;且標(biāo)簽的src=“http://127.0.0.1:8080/getNews?callback=appendHtml”
      var script = document.createElement('script')
      script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
      document.head.appendChild(script) //把這個標(biāo)簽插入頭部钦购;就會發(fā)送src請求
      document.head.removeChild(script) //發(fā)送請求后檐盟,這個script標(biāo)簽就沒用了,就立即刪除押桃;
    })
    
    //由于發(fā)送的請求里面有關(guān)鍵字callback=appendHtml
    //所以當(dāng)后臺處理請求時候會把數(shù)據(jù)用這個關(guān)鍵詞和括號鏈接如:“appendHtml(數(shù)據(jù))”
    //下面這個函數(shù)就是用來解析數(shù)據(jù)的葵萎;
    function appendHtml(news){
      var html = ''
      for(var i=0; i<news.length; i++){
        html += '<li>' + news[i] + '</li>'
      }
      console.log(html)
      $('.news').innerHTML = html
    }
    
    //封裝的document選擇器
    function $(selector){
      return document.querySelector(selector)
    }

  </script>

</body>
</html>

router.js的代碼

// 注意這里的代碼要放在單獨(dú)的 router.js 文件中


router.get('/getNews', function (req, res) {

  var news = [
    "海軍首批空中女戰(zhàn)勤加入戰(zhàn)斗序列",
    "運(yùn)20完成首次人員空運(yùn)試驗:乘客均為研發(fā)團(tuán)隊成員",
    "英媒稱中國將引領(lǐng)科技變革:新四大發(fā)明走向全球",
    "印巴軍隊交火未平息 兩國代表聯(lián)合國又上演舌戰(zhàn)",
    "朝鮮的這樣?xùn)|西比“地震”更加震動美國人",
    "鄭家概履新武警部隊參謀長 接替秦天",
    "從仕途起點清除遺毒 這個落馬副部問題多嚴(yán)重?",
    "蔡英文'樸實'午宴曝光:等于退休人員全家4天菜錢"
  ]

  var data = []
  for (var i = 0; i < 3; i++) {
    var index = parseInt(Math.random() * news.length)
    data.push(news[index])
    news.splice(index, 1) //為了避免隨機(jī)到重復(fù)元素唱凯,所以push完一個就刪除當(dāng)前
  }

  var cb = req.query.callback
  if (cb) {
    res.send(cb + '(' + JSON.stringify(data) + ')')
  } else {
    res.send(data)
  }
})

上面index.html中<script src="http://127.0.0.1:8080/getNews?callback=appendHtml">標(biāo)簽的src就是一個請求
把這串地址在瀏覽器打開可以看到返回的數(shù)據(jù)如下圖:

WX20170924-220702.png

其實其他域名地址下的js就是利用這調(diào)串?dāng)?shù)據(jù)調(diào)用寫好的js函數(shù)實現(xiàn)解析數(shù)據(jù)羡忘;實現(xiàn)跨域。


CORS

參考文章:阮一峰-跨域資源共享 CORS 詳解

CORS是一個W3C標(biāo)準(zhǔn)磕昼,全稱是"跨域資源共享"(Cross-origin resource sharing)卷雕。
它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請求票从,從而克服了AJAX只能同源使用的限制漫雕。
本文詳細(xì)介紹CORS的內(nèi)部機(jī)制。

簡介

CORS需要瀏覽器和服務(wù)器同時支持峰鄙。
目前浸间,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10吟榴。
整個CORS通信過程发框,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說梅惯,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣仿野。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源铣减,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求脚作,但用戶不會有感覺葫哗。
因此,實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器球涛。只要服務(wù)器實現(xiàn)了CORS接口劣针,就可以跨源通信。

基本流程

就是說當(dāng)我發(fā)送請求瀏覽器會默認(rèn)幫請求添加一個請求頭如圖下所示
(這都是由瀏覽器自己完成)

WX20170924-220929.png

上面的頭信息中亿扁,Origin字段用來說明捺典,本次請求來自哪個源(協(xié)議 + 域名 + 端口)。服務(wù)器根據(jù)這個值从祝,決定是否同意這次請求襟己。

因此只要后端代碼里面同意著了來源的網(wǎng)站請求數(shù)據(jù)就可以了!
后代碼只要在原來的基礎(chǔ)上加一個響應(yīng)頭表示我同意這個來源的網(wǎng)站向我請求數(shù)據(jù)(如下圖)

WX20170924-221005.png

此時http://api.bob.com這個網(wǎng)站就可以跨域向這個后端請求數(shù)據(jù)了

CORS與JSONP的比較

CORS與JSONP的使用目的相同牍陌,但是比JSONP更強(qiáng)大擎浴。
JSONP只支持GET請求,CORS支持所有類型的HTTP請求毒涧。JSONP的優(yōu)勢在于支持老式瀏覽器贮预,以及可以向不支持CORS的網(wǎng)站請求數(shù)據(jù)。


降域

由于第一次看視頻沒看懂契讲;最終原因是里面新出現(xiàn)的標(biāo)簽不了解這里順便把新的知識點也記錄一下仿吞;

首先什么是降域?

首先我們知道瀏覽器會阻止兩個不同的域名之間進(jìn)行數(shù)據(jù)訪問操作怀泊;
但是當(dāng)我有一個域名為:zeeliu.com 還有兩個子域名:a.zeeliu.com和b.zeeliu.com
由于瀏覽器的特性a.zeeliu.com和b.zeeliu.com之間也是不能互相訪問數(shù)據(jù)的(同一個爸爸也沒用)
但是現(xiàn)實情況是我們希望這兩個域名之間互相訪問
所以就有了一個`document.domain`的api

例如:

a.zeeliu.com下的文件問index.html
b.zeeliu.com下的文件問index.html
在兩個index.html下同時寫入js代碼document.domain="zeeliu.com"
這是域名都降為zeeliu.com
所以網(wǎng)頁a.zeeliu.com(a.zeeliu.com:80/index.html)
和網(wǎng)頁b.zeeliu.com(b.zeeliu.com:80/index.html)
之間就可以跨域數(shù)據(jù)交互了

下面我們通過代碼在瀏覽器中測試

由于我們在同一個文件夾下測試所以兩個index.html文件風(fēng)別用a.html和b.html代替

代碼如下(里面有解釋)

a.html

<body>
    <style>
        .ct{
          width: 910px;
          margin: auto;
        }
        .main{
          float: left;
          width: 450px;
          height: 300px;
          border: 1px solid #ccc;
        }
        .main input{
          margin: 20px;
          width: 200px;
        }
        .iframe{
          float: right;
        }
        iframe{
          width: 450px;
          height: 300px;
          border: 1px dashed #ccc;
        }
      </style>
      
<!-- //面是樣式從下面開始解讀 -->
      <div class="ct">
        <h1>使用降域?qū)崿F(xiàn)跨域</h1>
        <div class="main">
          <input type="text" placeholder="http://a.zeeliu.com:8080/a.html我是輸入框默認(rèn)顯示">
        </div>
        
        <!-- //ifame標(biāo)簽可以在當(dāng)前窗口建立一個子窗口茫藏;窗口顯示b.zeeliu.com:8080/b.html這個域名的內(nèi)容 -->
        <iframe src="http://b.zeeliu.com:8080/b.html" frameborder="0" ></iframe>
      </div>

      <script>
      //URL: http://a.zeeliu.com:8080/a.html
      //添加一個事件輸入框內(nèi)容變化出發(fā)(input事件)
      document.querySelector('.main input').addEventListener('input', function(){
        console.log(this.value);//在控制臺打印輸入框輸入的內(nèi)容
        //window.frames獲取當(dāng)前窗口中的所有子窗口得到一個類數(shù)組對象,其實就是針對選中<ifame>創(chuàng)建的子窗口
        //下面的代碼是選中窗口后在通過document.querySelector('input')選中b.html中的輸入框(input)然后把當(dāng)前窗口輸入的值(this.value)賦給b.html中的input
        //就是a.html中輸入什么b.html中就顯示什么
        window.frames[0].document.querySelector('input').value = this.value;
      })
      
      //降域
      //只有a.html和b.html中同時降域為zeeliu.com上面的input事件中的賦值操作才生效
      //也就是a.html通過跨域操作了b.html中的內(nèi)容
      document.domain = "zeeliu.com"
      </script>
</body>

b.html

<body>
        <style>
                html,body{
                    margin: 0;
                }
                input{
                    margin: 20px;
                    width: 200px;
                }
            </style>
            
                <input id="input" type="text"  placeholder="http://b.zeeliu.com:8080/b.html我是輸入框默認(rèn)顯示">
            <script>
            // URL: http://b.zeeliu.com:8080/b.html
             
            document.querySelector('#input').addEventListener('input', function(){
                //同a.html中相同這邊這邊輸入框輸入上面同樣賦值給那邊
                //window.parent選擇當(dāng)前前窗口的父窗口一般只有一個
                window.parent.document.querySelector('input').value = this.value;
            })
            //降域
            document.domain = 'zeeliu.com';
            </script>
</body>

看上面的代碼基本可以知道降域是怎么回事了霹琼;


下面是如何在本地建立服務(wù)器測試這個代碼(包括修改hosts的內(nèi)容)
首先把a(bǔ).html和b.html放在同一個文件夾下
通過前面學(xué)到修改hosts的方法添加兩個域名(如下圖)

WX20170924-221140.png

然后在當(dāng)前文件夾mock start創(chuàng)建模擬服務(wù)器

沒有降域情況下(document.domain被注釋)

如下圖
輸入的地址為a.zeeliu.com:8080/a.html
虛線框窗口引用的地址是b.zeeliu.com:8080/b.html
所以左邊輸入aaaa右邊沒有變化
【這是域不同 且沒有降域】

WX20170924-221303.png

如下圖
當(dāng)輸入的地址為b.zeeliu.com:8080/a.html及時沒用降域也能實現(xiàn)左右同步
【這是因為他們的域相同】

WX20170924-221346.png

降域情況下

如下圖
通過document.domain降域后及時域名不同也可以實現(xiàn)效果

WX20170924-221443.png

postMessage--------------------------------------

其實就是在不降域的情況a.html向b.html發(fā)送自己想給b.html的內(nèi)容务傲;然后b.html在接受這個內(nèi)容(你情我愿);反正b向a也是一樣

看代碼

a.html代碼

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>降域</title>
  <style>
    .ct{
      width: 910px;
      margin: auto;
    }
    .main{
      float: left;
      width: 450px;
      height: 300px;
      border: 1px solid #ccc;
    }
    .main input{
      margin: 20px;
      width: 200px;
    }
    .iframe{
      float: right;
    }
    iframe{
      width: 450px;
      height: 300px;
      border: 1px dashed #ccc;
    }
  </style>
</head>
<body>
  <div class="ct">
    <h1>使用postMessage實現(xiàn)跨域</h1>
    <div class="main">
      <input type="text" placeholder="http://a.zeeliu.com:8080/a.html">
    </div>
    <iframe src="http://b.zeeliu.com:8080/b.html" frameborder="0" ></iframe>
  </div>
  <script>

  //當(dāng)輸入框內(nèi)發(fā)生變化觸發(fā)事件  
  $('.main input').addEventListener('input', function(){
    console.log(this.value);
    //將當(dāng)前窗口的值通過.postMessage發(fā)送給window.frames[0]所選中的窗口
    //this.value是要發(fā)送的值枣申;(可以使其他的值)
    //'*':代表任何網(wǎng)站售葡;(當(dāng)輸入b.zeeliu.com:8080/b.html則只發(fā)給這個域名)
    window.frames[0].postMessage(this.value,'*');
  })

  //這個事件監(jiān)聽從b.html發(fā)送過來的數(shù)據(jù);
  //e.data就是接收到的數(shù)據(jù)(b.html中的.postMessage()發(fā)送過來的)
  window.addEventListener('message',function(e) {
      $('.main input').value = e.data
      console.log(e.data);
  });
  function $(id){
    return document.querySelector(id);
  }
  </script>
</body>
</html>

b.html代碼

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>降域</title>
    <style>
        html,body{
            margin: 0;
        }
        input{
            margin: 20px;
            width: 200px;
        }
    </style>
</head>
<body>
    <input id="input" type="text"  placeholder="http://b.zeeliu.com:8080/b.html">
    <script>
    $('#input').addEventListener('input', function(){
        //將當(dāng)前窗口的值通過.postMessage發(fā)送給window.parent所選中的窗口
        //this.value是要發(fā)送的值忠藤;(可以使其他的值)
        //'*':代表任何網(wǎng)站挟伙;(當(dāng)輸入a.zeeliu.com:8080/a.html則只發(fā)給這個域名)
        window.parent.postMessage(this.value, '*');
    })

    //這個事件監(jiān)聽從a.html發(fā)送過來的數(shù)據(jù);
    //e.data就是接收到的數(shù)據(jù)(a.html中的.postMessage()發(fā)送過來的)
    window.addEventListener('message',function(e) {
            $('#input').value = e.data
        console.log(e.data);
    });
    function $(id){
        return document.querySelector(id);
    }
    </script>
</body>
</html>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末模孩,一起剝皮案震驚了整個濱河市尖阔,隨后出現(xiàn)的幾起案子贮缅,更是在濱河造成了極大的恐慌,老刑警劉巖介却,帶你破解...
    沈念sama閱讀 222,681評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谴供,死亡現(xiàn)場離奇詭異,居然都是意外死亡齿坷,警方通過查閱死者的電腦和手機(jī)桂肌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來永淌,“玉大人崎场,你說我怎么就攤上這事∷熘” “怎么了谭跨?”我有些...
    開封第一講書人閱讀 169,421評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長答恶。 經(jīng)常有香客問我饺蚊,道長,這世上最難降的妖魔是什么悬嗓? 我笑而不...
    開封第一講書人閱讀 60,114評論 1 300
  • 正文 為了忘掉前任污呼,我火速辦了婚禮,結(jié)果婚禮上包竹,老公的妹妹穿的比我還像新娘燕酷。我一直安慰自己,他們只是感情好周瞎,可當(dāng)我...
    茶點故事閱讀 69,116評論 6 398
  • 文/花漫 我一把揭開白布苗缩。 她就那樣靜靜地躺著,像睡著了一般声诸。 火紅的嫁衣襯著肌膚如雪酱讶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,713評論 1 312
  • 那天彼乌,我揣著相機(jī)與錄音泻肯,去河邊找鬼。 笑死慰照,一個胖子當(dāng)著我的面吹牛灶挟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播毒租,決...
    沈念sama閱讀 41,170評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼稚铣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起惕医,我...
    開封第一講書人閱讀 40,116評論 0 277
  • 序言:老撾萬榮一對情侶失蹤耕漱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后曹锨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孤个,經(jīng)...
    沈念sama閱讀 46,651評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,714評論 3 342
  • 正文 我和宋清朗相戀三年沛简,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斥废。...
    茶點故事閱讀 40,865評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡椒楣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出牡肉,到底是詐尸還是另有隱情捧灰,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布统锤,位于F島的核電站毛俏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏饲窿。R本人自食惡果不足惜煌寇,卻給世界環(huán)境...
    茶點故事閱讀 42,211評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逾雄。 院中可真熱鬧阀溶,春花似錦、人聲如沸鸦泳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽做鹰。三九已至击纬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間钾麸,已是汗流浹背更振。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留喂走,地道東北人殃饿。 一個月前我還...
    沈念sama閱讀 49,299評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像芋肠,于是被迫代替她去往敵國和親乎芳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,870評論 2 361

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

  • 什么是同源策略 瀏覽器出于安全方面的考慮,只允許與本域下的接口交互奈惑。不同源的客戶端腳本在沒有明確授權(quán)的情況下吭净,不能...
    謝夢揚(yáng)_閱讀 439評論 0 0
  • 1.什么是同源策略瀏覽器出于安全方面的考慮,只允許與本域下的接口交互肴甸。不同源的客戶端腳本在沒有明確授權(quán)的情況下寂殉,不...
    24_Magic閱讀 499評論 0 0
  • 1: 什么是同源策略 最初,它的含義是指原在,A網(wǎng)頁設(shè)置的 Cookie友扰,B網(wǎng)頁不能打開,除非這兩個網(wǎng)頁"同源",所謂...
    好奇而已閱讀 303評論 0 0
  • 今天庶柿,我放下那么多的家務(wù)村怪,就想為你寫點東西,留下我們點點滴滴的回憶浮庐。未曾想甚负,為你寫的文字竟然得不到你的鼓...
    37度女王閱讀 285評論 0 0
  • 我只是害怕有那么一天我不再憤世嫉俗,也活成了別人的附屬品审残,而那一天到來之際我還沒有強(qiáng)大到跟韓寒一樣可以恣意...
    凌洛依閱讀 982評論 0 0