同源策略和跨域知識點學習

問題原由是在使用weibo api的時候,發現有一個報錯。weibo api是https協議,我本地是模擬的回調域名,而後進行數據通訊,本地http協議,因而乎就報錯了。出於對postMessage的不是很熟悉,藉此機會學習晚上一些本身的知識儲備。php

api.weibo.com/2/oauth2/authorize?client_id=******&response_type=token&d…ansport=html5&referer=http://www.unofficial.cn/demo/vuejs/demo.html:1 Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('https://www.unofficial.cn') does not match the recipient window's origin ('http://www.unofficial.cn').

同源策略

在這以前須要先熟悉一下這個概念,同源指請求協議相同,主機名相同,端口相同,涉及安全的策略。css

// 例如個人博客地址
    http://www.unofficial.cn/demo/postMessage/pm1.html      同
    http://www.unofficial.cn/demo/vuejs/index.html          同
    https://www.unofficial.cn/demo/postMessage/pm1.html     不一樣 協議不一樣
    http://blog.unofficial.cn/demo/postMessage/pm1.html     不一樣 主機名不一樣
    http://www.unofficial.cn:8080/demo/postMessage/pm1.html 不一樣 端口不一樣

容許跨域寫

表單提交,例如我模擬了一個表單提交到個人一個其它站點。 html

同源策略主要限制的是不一樣源之間的交互操做,對於跨域內嵌的資源不受該策略限制。前端

容許跨域嵌入

  • <script src="……"></script> 標籤嵌入腳本,語法錯誤信息只能在同源腳本中捕捉到(?)。vue

  • <link rel="stylesheet" href="……"> 標籤嵌入csshtml5

  • <img src="" alt=""> 標籤嵌入圖片jquery

  • <video></video> 和 <audio></audio> 標籤嵌入多媒體資源ajax

  • @font-facejson

  • <iframe src="……" frameborder="0"></iframe> 載入的任何資源。可使用x-frame-options消息頭來阻止這種形式的交互。後端

不容許跨域讀

須要注意的是,頁面內的引入的文件的域並不重要,重要的是加載該文件的頁面所在的域。例如說我在博客的首頁引入了 //cdn.bootcss.com/jquery/3.1.1/jquery.min.js 的jquery文件,這時 jquery.min.js 的源應該就是個人博客地址 http://www.unofficial.cn

  • iframe
    同域可讀可寫,跨域可讀不可寫

// 請求地址://www.unofficial.cn/demo/postmessage/pm2.html
<iframe src="pm2.html" frameborder="0"></iframe>
<iframe src="//blog.unofficial.cn/demo/postmessage/pm2.html" frameborder="0"></iframe>
<script>
    window.onload = function() { // 必須等待文檔加載結束才能獲取
        var iframe = document.getElementsByTagName('iframe');
        console.log(iframe[0].contentDocument); // 同源
        console.log(iframe[1].contentDocument); // 不一樣源
    }
</script>
// 不一樣源時使用contentWindow/contentDocument報錯
// pm1.html:12 Uncaught DOMException: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "http://www.unofficial.cn" from accessing a cross-origin frame.(…)
  • 同源

    iframe外部操做,主要經過contentDocument/contentWindow,iframe內部使用window.parent,若是隻是嵌套了一層可使用window.top,iframe多層嵌套可使用window.frameElement

    // 外部 -> 內
    
        var iframe = document.getElementsByTagName('iframe');
        // 舉例第一個
        iframe[0].contentDocument.getElementById('test').innerText = 123;
    
        // 內部 -> 外
        window.parent.getElementById('test').innerText = 123;
  • 跨域

    若是須要在跨域的狀況下傳遞參數怎麼操做呢?
    iframe內部操做,主要經過 location.hash

    // 外部傳遞一個123給內部
        var src = iframe[0].src;
        iframe[0].src = src.indexOf('#') != -1 ? src.split('#')[0].concat('#', 123) : src.concat('#', 123);
        // 而後內部監測hashChange,自動獲取hash值
    
        // 內部更改hash
        window.location.hash = 123;
        // 可是如何外部如何監控src的變化呢?
  • ajax

    • cors

      同域可讀可寫,跨域請求不能檢查到 Access-Control-Allow-Origin 的狀況下會被攔截。
      // www.unofficial.cn:4000
      // 跨域請求
      var url = "http://www.unofficial.cn/demo.php";
      var params = "lorem=ipsum&name=binny";
      
      var http = new XMLHttpRequest();
      http.open("POST", url, true);
      
      http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      
      http.onreadystatechange = function() {
      if(http.readyState == 4 && http.status == 200) {
          alert(http.responseText);
      }
      }
      http.send(params);
      > XMLHttpRequest cannot load http://www.unofficial.cn/demo.php. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://www.unofficial.cn:4000' is therefore not allowed access.
      
      上面錯誤提示能夠設置 `Access-Control-Allow-Origin` ,因而在header中添加設置便可實現跨域請求。
  • 參數介紹

    • Access-Control-Allow-Origin
      origin參數指定一個容許向該服務器提交請求的URI.對於一個不帶有credentials的請求,能夠指定爲'*',表示容許來自全部域的請求.

    Access-Control-Allow-Origin: http://www.unofficial.cn
    • Access-Control-Allow-Credentials
      它的值是一個布爾值,表示是否容許發送Cookie。默認是 true 容許的。 『實際測試沒發現,也許是方法還不對吧。』

    • Access-Control-Expose-Headers
      設置瀏覽器容許訪問的服務器的頭信息的白名單。若是沒有設置白名單的,默認狀況下只能獲取 Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma的值,沒設置返回 null,不然會獲得如下提示:

    Refused to get unsafe header "X-Powered-By"

    例如:

    // 服務端設置
    Access-Control-Expose-Headers: X-Powered-By

    前端能夠這樣獲取到 X-Powered-By 的屬性值

    var http = new XMLHttpRequest();
    
        http.getResponseHeader('X-Powered-By'); //
    • Access-Control-Max-Age
      設置預請求時間。便是設置 OPTION 的時間。

    • Access-Control-Allow-Methods
      設置容許的請求方法。

jsonp

cors的方式能夠發起post請求,或者說其它形式的請求,可是jsonp只能使用get的方式獲取數據。

<script>
    function abc(data) {
        console.log(data);
    }abc('{"abc":"123"}');
</script>

<script src="http://www.unofficial.cn/test/demo.php?callback=abc"></script>

簡單說就是定義好回調處理方法,把回調函數的名稱傳遞給後端,後端拿到數據名稱後返回會的數據就是對於回調方法的執行。

<script src="http://www.unofficial.cn/test/demo.js"></script>
/**
 * demo.js的內容
 * abc({"abc":"123"});
 */

什麼是postMessage

postMessage是window對象的一個屬性,widow.postMessage是一個安全的跨源通訊協議。當且僅當執行腳本的頁面使用相同的協議(一般都是 http)、相同的端口(http默認使用80端口)和相同的 host(兩個頁面的 document.domain 的值相同)時,才容許不一樣頁面上的腳本互相訪問。 window.postMessage 提供了一個可控的機制來安全地繞過這一限制,當其在正確使用的狀況下。

  • iframe的狀況下咱們能夠這樣使用,等待頁面加載結束時傳參數到指定源。

// localhost ① pm1.html頁面中存在一個跨域iframe引用
<iframe src="//www.unofficial.com/demo/postMessage/pm2.html" frameborder="0"></iframe>
<script>
    window.onload = function() {
        window.frames[0].postMessage('some messages', '*'); // * 跨域是固定的targetOrigin(必須指明協議、主機名、端口) http://www.unofficial.com
    }
</script>

// www.unofficial.cn pm2.html中咱們跨域監聽 `message` 獲取 `postmessage` 傳過來的數據。  
<script>
    window.addEventListener('message', function(event) {
        if(event.origin.test('//localhost/')) {
            console.log(event.data);
        }
    })
</script>
  • window.open的狀況下就須要特殊處理一下了

// localhost ② window.open
<script>
    function openAPage() {
        // 同源的狀況下能夠判斷頁面是否加載結束
        var openPage = window.open('//localhost/demo/postMessage/pm2.html');
        openPage.onload = function() {
            openPage.postMessage('some messages', '*');
        }
        // 不一樣源的狀況下,使用setTimeout或者setInterval
        var openPage = window.open('//www.unofficial.com/demo/postMessage/pm2.html');
        setTimeout(function() {
            openPage.postMessage('some messages', '*');
        }, 0)
    }
</script>

延遲多長時間執行?頁面加載時間是多長,這個不是很好判斷,setTimeout須要略估計一個時間,待open的頁面加載完成了再postMessage(應該不比徹底加載就能夠postMessage了)。要否則就是定時器,定時推一次,直接觸發事件後使用window.opener取消定時器。

總結

問題基本都是在過程當中發現一個學習一個,對於沒有太多場景的學習,只能這樣慢慢積累。

參考資料

相關文章
相關標籤/搜索