說說跨域那些事兒

首先糾正一個誤區,跨域並不是瀏覽器限制了發起跨站請求的這種能力,偏偏相反,咱們能夠發出請求,服務端也能夠接收到請求並正常返回數據,只不過在返回以後瀏覽器會阻止非同源數據(response),從而在控制檯打出一系列報錯信息。javascript

原文地址:說說跨域那些事兒,遷移到簡書上來和你們共享php

兼容性查找

文章中會涉及一系列兼容性的圖解(mdn & can i use)和一些專有名詞(mdn),能夠經過兩個渠道來查看html

跨域類型

定義就不說了,從字面也能夠看其含義,首先咱們認識下哪些狀況屬於跨域,能夠分爲如下幾點:jquery

  • 協議不一樣,如http, https;git

  • 端口不一樣;github

  • 主域相同,子域不一樣;算法

  • 主域不一樣;json

  • ip地址和域名之間也算是跨域,瀏覽器不會自動作ip域名的映射;

解決方案

  • document.domain

  • window.name

  • jsonp

  • postMessage

  • cors

document.domain

  • 關鍵點

    • 跨域分爲兩種,一種xhr不能訪問不一樣源的文檔,另外一種是不一樣window之間不能進行交互操做;

    • document.domain主要是解決第二種狀況,且只能適用於主域相同子域不一樣的狀況;

    • document.domain的設置是有限制的,咱們只能把document.domain設置成自身或更高一級的父域,且主域必須相同。例如:a.b.example.com中某個文檔的document.domain能夠設成a.b.example.com、b.example.com 、example.com中的任意一個,可是不能夠設成c.a.b.example.com,由於這是當前域的子域,也不能夠設成baidu.com,由於主域已經不相同了。

  • 兼容性:全部瀏覽器都支持;

  • 優勢:

    • 能夠實現不一樣window之間的相互訪問和操做;

  • 缺點:

    • 只適用於父子window之間的通訊,不能用於xhr;

    • 只能在主域相同且子域不一樣的狀況下使用;

  • 使用方式

    • a(當前頁面或父頁面)頁面中加入document.domain = 'example.com';

    • b(當前頁面或子頁面)頁面中加入document.domain = 'example.com';

    • a頁面訪問b頁面裏面的數據或者方法;

window.name

  • 關鍵點:window.name在頁面的生命週期裏共享一個window.name;

  • 兼容性:全部瀏覽器都支持;

  • 優勢:

    • 最簡單的利用了瀏覽器的特性來作到不一樣域之間的數據傳遞;

    • 不須要前端和後端的特殊配製;

  • 缺點:

    • 大小限制:window.name最大size是2M左右,不一樣瀏覽器中會有不一樣約定;

    • 安全性:當前頁面全部window均可以修改,很不安全;

    • 數據類型:傳遞數據只能限於字符串,若是是對象或者其餘會自動被轉化爲字符串,以下;

  • 使用方式:修改window.name的值便可;

jsonp

  • 關鍵點:瀏覽器對XHR作了同源策略,但並無將這種方式延續到script上(其實還有iframe,img等),從而能夠利用動態script標籤技術來作到跨域請求的做用。至於爲何會這樣設計,本人也不太清楚,有多是歷史遺蹟(漏洞),有多是某些方面的技術瓶頸,也有多是爲了知足某些需求專門定製的,總之這項技術方案咱們過去能夠用,如今能夠用就ok,至於未來應該也是會存在的,畢竟如今已經應用在不少家站點上,就算會廢棄,也會有一段時間迭代。

  • 兼容性:全部瀏覽器都兼容這種方式;

  • 優勢:很明顯前端能夠很輕鬆的作到跨域請求;

  • 缺點

    • 只能經過GET方式請求,一方面是參數長度有限制,二是安全性比較差;

    • 後端須要知道前端的cb是什麼樣的結構,主要在參數和回調名;

    • 後端須要進行參數和cb的拼接而後才能執行;

  • 使用方式

/** 前端生成script標籤,並將src中傳入須要執行的callback **/
<script type="text/javascript">
    var ele = document.createElement('script');
    ele.type = "text/javascript"
    ele.src = "http://example.com?jsonp=cb";
    document.body.appendChild(ele);
</script>


/** 後端接到參數後給callback加入參數並執行 **/
<?php
    $cb = $_GET['cb'];
    $data = array('test1', 'test2', 'test3');
    echo $cb.'('.json_encode($data).')';
?>

postMessage

  • 關鍵點:postMessage是h5引入的一個新概念,如今也在進一步的推廣和發展中,他進行了一系列的封裝,咱們能夠經過window.postMessage的方式進行使用,並能夠監聽其發送的消息;

  • 兼容性:下圖是postMessage的兼容圖,移動端能夠放心用,可是pc端須要作降級處理,具體能夠根據文中介紹的這幾種跨域方式來則情選擇;
    poseMessage兼容性

  • 優勢

    • 不須要後端介入就能夠很是簡單的的作到跨域,一個函數外加兩個參數(請求url,發送數據)就能夠搞定;

    • 移動端兼容性好;

  • 缺點

    • 沒法作到一對一的傳遞方式:監聽中須要作不少消息的識別,因爲postMessage發出的消息對於同一個頁面的不一樣功能至關於一個廣播的過程,該頁面的全部onmessage都會收到,因此須要作消息的判斷;

    • 安全性問題:三方能夠經過截獲,注入html或者腳本的形式監聽到消息,從而可以作到篡改的效果,因此在postMessage和onmessage中必定要作好這方面的限制;

    • 發送的數據會經過結構化克隆算法進行序列化,因此只有知足該算法要求的參數纔可以被解析,不然會報錯,如function就不能看成參數進行傳遞;

  • 使用方式:下面是前段時間寫的一個通訊的函數,sendMessage_負責發送消息,bindEvent_負責消息的監聽並處理,能夠經過代碼來作一個大體瞭解;

Storage.prototype.sendMessage_ = function(type, params, fn) {
    if (this.topWindow) {
        this.handleCookie_(type, params, fn);
        return;
    }
    var eventId = this.addToQueue_(fn, type);
    var storageIframe = document.getElementById('mip-storage-iframe');
    var element = document.createElement("a");
    element.href = this.origin;
    var origin = element.href.slice(0, element.href.indexOf(element.pathname) + 1);        

    storageIframe.contentWindow.postMessage({
        type: type,
        params: params,
        eventId: eventId
    }, origin);
}

Storage.prototype.bindEvent_ = function() {
    window.onmessage = function (res) {
        // 判斷消息來源            
        if (window == res.source.window.parent &&
            res.data.type === this.messageType.RES &&
            window.location.href.match(res.origin.host).length > 0) {                
            var fn = this.eventQueue[res.data.eventId];
            fn && fn();
            delete this.eventQueue[res.data.eventId];
            // reset id
            var isEmpty = true;
            for (var t in this.eventQueue) {
                isEmpty = false;
            }
            if (isEmpty) {                    
                this.id = 0;
            }
        }                    
    }.bind(this);
}

cors

  • 關鍵點:cors是一種經過先後端http header配置來進行跨域的一種方式;

  • 兼容性:若是不考慮pc端的IE,移動端的opera的話那兼容性仍是不錯的,針對ie和opera能夠作適當的降級處理;
    poseMessage兼容性

  • 安全策略

    • 請求

      • origin:經過http頭中的origin判斷域名是不是容許的;

      • Example-Same-origin:若是http origin不存在,最好可以本身在請求頭中加入該參數來標示是不是同源,true表示請求來自於同域名下(同域名下請求不帶origin);若是該字段存在而且爲true則容許請求接口,不然禁止;

      • Example_source_origin:該參數同origin,是在origin不存在的狀況下用來標示請求來源的url

    • 返回

      • Access-Control-Allow-Origin: originorigin表示容許哪些網站請求,不建議設置爲*;

      • Access-Control-Expose-Headers:Example-Access-Control-Allow-Source-Origin,容許http返回中包含該字段,能夠經過這種方式在返回頭中加入自定義字段,如該例子中的Example-Access-Control-Allow-Source-Origin;

  • 優勢

    • 前端方便很多,只須要發請求而不用考慮跨域問題;

    • 安全性可以得以控制和保障;

  • 缺點

    • 兼容性不全面,須要作降級處理;

  • 使用方式

    • 正常請求便可,不管是你要用xhr,仍是用一些封裝好的組件,如fetchfetchJsonp,亦或是jquery一類的技術都可;

    • 後端在response時須要設置必定的配置參數,並保證安全策略,具體方案能夠參照下面安全策略模塊;

相關文章
相關標籤/搜索