關於javascript跨域及JSONP的原理與應用

1、同源策略

同源策略,它是由Netscape提出的一個著名的安全策略,如今全部的可支持javascript的瀏覽器都會使用這個策略。javascript

爲何須要同源策略,這裏舉個例子:php

假設如今沒有同源策略,會發生什麼事情呢?你們知道,JavaScript能夠作不少東西,好比:讀取/修改網頁中某個值。恩,你如今打開了瀏覽器,在一 個tab窗口中打開了銀行網站,在另一個tab窗口中打開了一個惡意網站,而那個惡意網站掛了一個的專門修改銀行信息的JavaScript,當你訪問 這個惡意網站而且執行它JavaScript時,你的銀行頁面就會被這個JavaScript修改,後果會很是嚴重!而同源策略就爲了防止這種事情發生.css

好比說,瀏覽器的兩個tab頁中分別打開了http://www.baidu.com/index.X19Xhtml和http: //www.google.com/index.html,其中,JavaScript1和JavaScript3是屬於百度的腳本,而 JavaScript2是屬於谷歌的腳本,當瀏覽器的tab1要運行一個腳本時,便會進行同源檢查,只有和www.baidu.com同源的腳本才能被執 行,所謂同源,就是指域名、協議、端口相同。因此,tab1只能執行JavaScript1和JavaScript3腳本,而JavaScript2不能 執行,從而防止其餘網頁對本網頁的非法篡改。html

2、什麼是JSONP?

JSONP(JSON with Padding)是一個非官方的協議,它容許在服務器端集成Script tags返回至客戶端,經過javascript callback的形式實現跨域訪問(這僅僅是JSONP簡單的實現形式)。前端

3、爲何使用JSONP?

因爲 JSON 只是一種含有簡單括號結構的純文本,所以許多通道均可以交換 JSON 消息。由於同源策略的限制,們不能在與外部服務器進行通訊的時候使用 XMLHttpRequest。而JSONP是一種能夠繞過同源策略的方法,即經過使用 JSON 與 < script> 標記相結合的方法,從服務端直接返回可執行的JavaScript函數調用或者JavaScript對象。html5

其實 jsonp 是個很簡單的一個東西。主要是利用了 <script/>標籤對javascript文檔的動態解析來實現。(其實也能夠用eval函數)。java

<script type="text/javascript">
    function jsonpCallback(result) {
        alert(result.msg);
    }
</script>
<script type="text/javascript" src="http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback"></script>

其中 jsonCallback 是客戶端註冊的,獲取跨域服務器上的json數據後,回調的函數。jquery

http://crossdomain.com/jsonServerResponse?jsonp=jsonpCallback

這個 url 是跨域服務器取 json 數據的接口,參數爲回調函數的名字,返回的格式爲:web

jsonpCallback({ msg:'this  is  json  data'})

Jsonp原理:ajax

首先在客戶端註冊一個callback, 而後把callback的名字傳給服務器。此時,服務器先生成 json 數據。而後以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 jsonp.

最後將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。

客戶端瀏覽器,解析script標籤,並執行返回的javascript文檔,此時數據做爲參數,傳入到了客戶端預先定義好的 callback 函數裏.(動態執行回調函數) .

其實說白了,就是客戶端定義一個函數(如a),請求地址後服務器端返回的結果是調用a函數,須要的數據都放在了a函數的參數裏面。

demo:

應爲它用到的只是全部 HTML 元素中一個簡單的 script 元素。看到這是否是以爲愈加奇怪了?不要緊,繼續看下去就會茅廁(塞)頓開的,嘿嘿~來看個例子吧:
demo.html:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo</title>
</head>
<body>
<script type="text/javascript">
    function say(words) {
       alert(words);
    }
</script>
<script type="text/javascript" src="demo.js"></script>
</body>
</html>

demo.js:

say("Hello, everyone!");

運行 demo.html 文件後,是否是看到寫着「Hello, everyone!」的警告框了?你可能會以爲這個例子很簡單,沒什麼了不得的,甚至會在想:這和 JSONP 有關係嗎?那麼,我能夠很確定的告訴你:有關係!並且,這個例子實際上就是 JSONP 的原型!你也許會想到,JSONP 不是訪問遠程數據的嗎?對,試想一下,若是 demo.js 文件在其它域的服務器上呢?結果會不會出現問題?我也能夠很負責的告訴你:不會!你能夠將上面例子中的 demo.js 更改成:http://demo.hpyer.cn/php/jsonp.php?callback=say 再試一下。
如今,聰明的你應該已經明白 JSONP 究竟是怎麼回事了,那麼,再來解釋一下本節開頭第一句話吧。看過 demo.js 文件的內容,應該知道,其只是對一個函數(一般稱之爲:回調函數)的調用,而須要交互的數據則經過參數形勢進行返回。因此經過 JSONP 訪問的服務器須要提供一個能夠設置回調函數名的接口,就像 http://demo.hpyer.cn/php/jsonp.php?callback=say 中的 callback,因此,綜上所述 JSONP 是須要服務器端的支持的。附上 jsonp.php 的源碼:

<?php
$type = isset($_GET['type']) ? $_GET['type'] : '';
$callback = isset($_GET['callback']) ? $_GET['callback'] : '';
$json = '';
if ($type == 'json') {
   $json = '{
   "Image": {
   "Width": 800,
   "Height": 600,
   "Title": "View from 15th Floor",
   "Thumbnail": {
   "Url": "http://www.example.com/image/481989943",
   "Height": 125,
   "Width": "100"
   },
   "IDs": [116, 943, 234, 38793]
   }
   }';
} else {
   $json = '"Hello, everyone!"';
}
if (!empty($callback)) {
   $json = $callback . '(' . $json . ')';
}
echo $json;

jquery 中的應用:
自 1.2 版本起,jQuery 加入了對 JSONP 的支持。咱們能夠很容易的利用 $.getJSON() 方法(或者其它基於 $.ajax() 的方法),來跨域加載 JSON 數據。來個例子吧:

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Demo</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
function do_jsonp() {
   $.getJSON("http://demo.hpyer.cn/php/jsonp.php?type=json&callback=?",
   function(data) {
   $('#result').val('data.Image.IDs: ' + data.Image.IDs);
   });
}
</script>
</head>
<body>
<a href="javascript:do_jsonp();">Click me</a><br />
<textarea id="result" cols="50" rows="5"></textarea>
</body>
</html>

你可能注意到上面的例子中,url 被寫成了 http://demo.hpyer.cn/php/jsonp.php?type=json&callback=?,須要說明的是,這個問號會被 jQuery 自動替換爲回調函數的函數名(若是是一個匿名函數,jQuery 會自動生成一個帶時間戳的函數名)。

看我在項目中的一個例子:

//定義Ajax函數
function ajaxFun() {
    var timeStamp = Math.floor(new Date().getTime() / 1000);
    var url = "http://apiso.alidemo.3gpp.cn/httpserver/cp/yisou/ali_feedback_interface.php?callback=jsonpCallback&feedbacktype=add&type=" + feedbackNumber + "&book=" + me.mixedInfo.title + "&author=" + me.mixedInfo.author + "&chapter=" + me.mixedInfo.cname + "&chapterid=" + me.mixedInfo.cid + "&questiondesc=" + text + "&platform=1&t=" + timeStamp + "&sn=" + md5("d30fcd1a9f1900fa049b4766e0a275e1" + timeStamp);
    var scriptObj = document.createElement("script");
    scriptObj.src = url;
    scriptObj.id = "jsonpScript";
    document.body.appendChild(scriptObj);
    //jsonp回調函數,jsonpCallback必須爲全局函數,由於jsonp返回的是在全局環境中執行函數的語句,即jsonpCallback(data)
    window.jsonpCallback = function(data) {
        switch (data.code) {
            case "1":
                novel.readerPrompt('提交成功,即將返回……', 1, function() {
                    window.history.go(-1);
                });
                break;
            case "0":
                novel.readerPrompt('提交失敗。', 2);
                break;
            case "900":
                novel.readerPrompt('提交失敗,驗證失敗。', 2);
                break;
        }
        //成功後刪除scriptObj,後面的setTimeout就不會執行了
        if (document.getElementById("jsonpScript")) {
            document.body.removeChild(scriptObj);
        }
    }
    //設置超時,超時的話直接顯示提交成功
    setTimeout(function() {
        if (document.getElementById("jsonpScript")) {
            document.body.removeChild(scriptObj);
            novel.readerPrompt('提交成功,即將返回……', 1, function() {
                window.history.go(-1);
            });
        }
    }, 2000);
}
ajaxFun();

via WEB前端開發

相關文章
相關標籤/搜索