通過(guò)一篇文章由淺入深的理解JSONP并拓展
簡(jiǎn)單描述
JSONP 是 JSON with padding(填充式 JSON 或參數(shù)式 JSON)的簡(jiǎn)寫。
JSONP實(shí)現(xiàn)跨域請(qǐng)求的原理簡(jiǎn)單的說(shuō),就是動(dòng)態(tài)創(chuàng)建<script>標(biāo)簽,然后利用<script>的src 不受同源策略約束來(lái)跨域獲取數(shù)據(jù)。
JSONP 由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù)?;卣{(diào)函數(shù)是當(dāng)響應(yīng)到來(lái)時(shí)應(yīng)該在頁(yè)面中調(diào)用的函數(shù)?;卣{(diào)函數(shù)的名字一般是在請(qǐng)求中指定的。而數(shù)據(jù)就是傳入回調(diào)函數(shù)中的 JSON 數(shù)據(jù)。
動(dòng)態(tài)創(chuàng)建<script>標(biāo)簽,設(shè)置其src,回調(diào)函數(shù)在src中設(shè)置:
var script = document.createElement("script");
script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);
在頁(yè)面中,返回的JSON作為response參數(shù)傳入回調(diào)函數(shù)中,我們通過(guò)回調(diào)函數(shù)來(lái)來(lái)操作數(shù)據(jù)。
function handleResponse(response){
// 對(duì)response數(shù)據(jù)進(jìn)行操作代碼
}
上面是簡(jiǎn)單直接的對(duì)JSONP 的描述,可能有些人不是很懂,我們下面一步一步分析
層層深入
先通過(guò)一個(gè)簡(jiǎn)單的實(shí)例簡(jiǎn)單的理解一下同源策略的作用
首先我們?cè)诒镜貙懭?/p>
1.html
<!DOCTYPE html>
<html>
<head>
<title>GoJSONP</title>
</head>
<body>
<script type="text/javascript">
function jsonhandle(data){
alert("age:" + data.age + "name:" + data.name);
}
</script>
<script type="text/javascript" src="jquery-3.3.1.min.js">
</script>
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type : "get",
url : "http://check.k0rz3n.com/test.php?id=1",
dataType: "jsonp",//指定我們的請(qǐng)求是一個(gè) jsonp 的請(qǐng)求
success : function(data) {//success 指定的是默認(rèn)的回調(diào)函數(shù)
jsonhandle(data);
}
});
});
</script>
</body>
</html>
我服務(wù)器上的test.php會(huì)返回json 格式的數(shù)據(jù)給客戶端
test1.php
<?php
header('Content-Type:application/json; charset=utf-8');
$data = array('age'=>19,'name'=>'jianshu');
exit(json_encode($data));
?>
如果正常訪問(wèn)的話,那么我們的瀏覽器應(yīng)該會(huì)彈出對(duì)話框,結(jié)果我們會(huì)得到這樣的結(jié)果

此處輸入圖片的描述
可以看到,瀏覽器發(fā)現(xiàn)這是一個(gè)跨域的請(qǐng)求,但是他在服務(wù)器的返回頭中缺沒(méi)有發(fā)現(xiàn)
Access-Control-Allow-Origin 值允許 http://localhost 的訪問(wèn),于是就攔截了。
也就是說(shuō),雖然瀏覽器受到了同源策略的限制,不允許實(shí)現(xiàn)跨域訪問(wèn),但是由于在開(kāi)發(fā)過(guò)程中前后端的交互過(guò)程中不可避免地會(huì)涉及到跨域的請(qǐng)求(設(shè)計(jì)同源策略的人想必也發(fā)現(xiàn)了這個(gè)問(wèn)題),于是設(shè)計(jì)者給我們留了一個(gè)后門,就是只要服務(wù)器響應(yīng)頭中返回允許這個(gè)源的選項(xiàng),那么跨域請(qǐng)求就會(huì)成功。(這里糾正一個(gè)誤區(qū),不要認(rèn)為瀏覽器默認(rèn)支持同源策略就意味著不同源的請(qǐng)求就不能發(fā)出去,其實(shí)還是能發(fā)出去的,只是要看響應(yīng)頭而已。)
我們知道在頁(yè)面中有幾個(gè)東西是對(duì)同源策略免疫的,那就是 <img> 的src 、<link> 的 href 還有就是<script>的 src , JSONP 就是利用 script 標(biāo)簽的sec 屬性實(shí)現(xiàn)跨區(qū)域請(qǐng)求的
script標(biāo)簽的請(qǐng)求不論是不是同源一律不受同源策略的限制,那我們就找到了解決跨域訪問(wèn)的方法(似乎這個(gè)方法一開(kāi)始就存在…..)
我們改變一下代碼,本地直接通過(guò)script標(biāo)簽請(qǐng)求服務(wù)器上的js,js 的內(nèi)容就是調(diào)用參數(shù)已經(jīng)傳進(jìn)去的本地的js函數(shù)
2.html
<!DOCTYPE html>
<html>
<head>
<title>GoJSONP</title>
</head>
<body>
<script type="text/javascript">
function jsonhandle(data){
alert("age:" + data.age + "name:" + data.name);
}
</script>
<script type="text/javascript" src="jquery-3.3.1.min.js">
</script>
<script type="text/javascript" src="http://check.k0rz3n.com/remote.js"></script>
</body>
</html>
remote.js
jsonhandle({
"age" : 15,
"name": "John",
})
注意:
(1)遠(yuǎn)程的js 代碼不需要script標(biāo)簽
(2)這其實(shí)也給了我們一些啟示,就是我們使用 callback 函數(shù)請(qǐng)求的頁(yè)面實(shí)際上類型是javascript 的類型,我們可以在這里看一下瀏覽器會(huì)將哪些類型當(dāng)做 javascript 解析:https://mathiasbynens.be/demo/javascript-mime-type
下圖可以看到我們成功利用<script>實(shí)現(xiàn)了跨域的訪問(wèn)。

此處輸入圖片的描述
那JSONP和這個(gè)有啥關(guān)系,感覺(jué)已經(jīng)實(shí)現(xiàn)跨域了還沒(méi)有提到一點(diǎn)JSONP,上面說(shuō)JSONP是基于script標(biāo)簽的,個(gè)人感覺(jué)JSONP的優(yōu)勢(shì)就是能夠?qū)崿F(xiàn)呢比較方便的函數(shù)選擇,傳一個(gè)參數(shù)就行了,不用像直接調(diào)用那樣必須要換js文件。
真相浮現(xiàn)
先用下面的代碼模擬jsonp的調(diào)用過(guò)程方便大家更好的理解jsonp的運(yùn)行過(guò)程
3.html
<!DOCTYPE html>
<html>
<head>
<title>GoJSONP</title>
</head>
<body>
<script type="text/javascript">
function jsonhandle(data){
alert("age:" + data.age + "name:" + data.name);
}
</script>
<script type="text/javascript" src="jquery-3.3.1.min.js">
</script>
<script type="text/javascript">
$(document).ready(function(){
var url = "http://check.k0rz3n.com/test1.php?id=1&callback=jsonhandle";
var obj = $('<script><\/script>');
obj.attr("src",url);
$("body").append(obj);
});
</script>
</body>
</html>
test1.php
<?php
$data = array(
'age' => 20,
'name' => 'dada',
);
$callback = $_GET['callback'];
echo $callback."(".json_encode($data).")";
return;
我們?cè)趕cipt標(biāo)簽里面給出的鏈接是我遠(yuǎn)程服務(wù)器的一個(gè)php的代碼,我給這個(gè)文件傳遞了一個(gè)參數(shù),作為我要調(diào)用的函數(shù)。服務(wù)器接收到這個(gè)參數(shù)以后把它當(dāng)做函數(shù)名,并給這個(gè)函數(shù)傳遞了一個(gè)json的值作為用戶調(diào)用的函數(shù)的參數(shù),最終實(shí)現(xiàn)調(diào)用
下面是調(diào)用成功的截圖

實(shí)際上,jquery 給我們提供了現(xiàn)成的接口,我們可以不用這么麻煩
4.html
<!DOCTYPE html>
<html>
<head>
<title>GoJSONP</title>
</head>
<body>
<script type="text/javascript" src="jquery-3.3.1.min.js"></script>
<script type="text/javascript">
function jsonhandle(data){
alert("age:" + data.age + "name:" + data.name);
}
</script>
<script type="text/javascript">
$(document).ready(function(){
$.ajax({
type : "get",
url : "http://check.k0rz3n.com/test1.php?id=1",
dataType: "jsonp",
jsonp:"theFunction", //指定回調(diào)函數(shù)在 URL 中的參數(shù)名(不指定默認(rèn)為 callback)
jsonpCallback: "jsonhandle",//指定回調(diào)函數(shù)名稱(如果不指定,服務(wù)器會(huì)隨機(jī)分配一個(gè)jQueryxxx 的名字)
success : function(data) {
console.info("調(diào)用success");
}
});
});
</script>
</body>
</html>
這時(shí)候的請(qǐng)求的 URL 就像下面這個(gè)樣子:
http://check.k0rz3n.com/test1.php?id=1&theFunction=jsonhandle
服務(wù)器端頁(yè)面為:
test2.php
<?php
$data = array(
'age' => 20,
'name' => 'dada',
);
$callback = $_GET['theFunction'];
echo $callback."(".json_encode($data).")";
return;
怎么樣,大概理解了吧,其實(shí)可以用一個(gè)非常形象的例子說(shuō)明:
幼稚園吃午飯,小明吧貼有自己名字的碗(回調(diào)函數(shù))給了幼稚園阿姨(服務(wù)器),阿姨給小明盛好飯(json參數(shù))以后又把碗還給了小明。
就是這樣的一個(gè)過(guò)程。
相關(guān)拓展:JSONP攻擊
1.JSONP 跨域劫持
實(shí)際上就是由于服務(wù)器端對(duì)JSONP 的請(qǐng)求來(lái)源的檢查不嚴(yán)格導(dǎo)致的
攻擊者模擬用戶向有漏洞的服務(wù)器發(fā)送JSONP請(qǐng)求,然后就獲取到了用戶的某些信息,再將這些信息發(fā)送到攻擊者可控的服務(wù)器
2.JSONP 跨域劫持token 實(shí)現(xiàn)CSRF
通過(guò) jsonp 發(fā)起請(qǐng)求,得到泄露的 csrf_token 然后,利用這個(gè)token 實(shí)現(xiàn)CSRF 攻擊
3.Referer 頭的繞過(guò)
在攻擊過(guò)程中可能會(huì)涉及到 referer 頭的繞過(guò)
- data:URL
為了逃避他的檢測(cè)我們可以選擇不發(fā)送referer這個(gè)頭,那么怎么做呢?這就涉及到 data:URL 頭
為了構(gòu)造一個(gè)不帶HTTP Referer的請(qǐng)求,我們可以濫用data URI方案。因?yàn)槲覀冋谔幚淼拇a包含了引號(hào),雙引號(hào),以及其他一些被阻止的語(yǔ)句,接著使用base64編碼我們的payload(回調(diào)函數(shù)定義以及腳本包含)
data:text/plain;base64our_base64_encoded_code:
以下3個(gè)HTML標(biāo)簽允許我們使用data URI方案:
iframe (在src屬性中) – Internet Explorer下不工作 embed (在src屬性中) – Internet Explorer及Microsoft Edge下不工作 object (在data屬性中) – Internet Explorer及Microsoft Edge下不工作
2.從HTTPS向HTTP發(fā)起請(qǐng)求
如果目標(biāo)網(wǎng)站可以通過(guò)HTTP訪問(wèn),也可以通過(guò)將我們的代碼托管在一個(gè)HTTPS頁(yè)面來(lái)避免發(fā)送HTTP Referer。如果我們從HTTPS頁(yè)面發(fā)起一個(gè)HTTP請(qǐng)求,瀏覽器為了防止信息泄漏是不會(huì)發(fā)送Referer header。以上我們要將惡意代碼托管在一個(gè)啟用了HTTPS的站點(diǎn)。
注意:由于mixed-content安全機(jī)制,在瀏覽器默認(rèn)設(shè)置下是不會(huì)工作的。需要受害者手動(dòng)允許瀏覽器發(fā)出的安全警告。
參考鏈接
- https://blog.csdn.net/u011897301/article/details/52679486
- https://blog.csdn.net/u014607184/article/details/52027879
- http://www.91ri.org/13407.html
- http://www.freebuf.com/articles/web/70025.html
- http://www.freebuf.com/articles/web/126347.html
- https://www.cnblogs.com/chiangchou/p/jsonp.html
總結(jié)
到此這篇關(guān)于JSONP拓展的文章就介紹到這了,更多相關(guān)理解JSONP并拓展內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Cropper.js進(jìn)階之裁剪后保存至服務(wù)器實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Cropper.js進(jìn)階之裁剪后保存至服務(wù)器實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
JS實(shí)現(xiàn)超簡(jiǎn)單的仿QQ折疊菜單效果
這篇文章主要介紹了JS實(shí)現(xiàn)超簡(jiǎn)單的仿QQ折疊菜單效果,可實(shí)現(xiàn)鼠標(biāo)滑過(guò)列表展開(kāi)的QQ折疊菜單效果,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-09-09
uni-app和原生小程序混合開(kāi)發(fā)的具體實(shí)現(xiàn)過(guò)程
最近項(xiàng)目中遇到了一些功能需要與原生進(jìn)行混合開(kāi)發(fā),所以下面這篇文章主要給大家介紹了關(guān)于uni-app和原生小程序混合開(kāi)發(fā)的具體實(shí)現(xiàn)過(guò)程,需要的朋友可以參考下2022-07-07
JavaScript設(shè)置名字輸入不合法的實(shí)現(xiàn)方法
這篇文章主要介紹了JavaScript設(shè)置名字輸入不合法的方法,需要的朋友可以參考下2017-05-05
javascript+HTML5 canvas繪制時(shí)鐘功能示例
這篇文章主要介紹了javascript+HTML5 canvas繪制時(shí)鐘功能,結(jié)合實(shí)例形式分析了javascript+HTML5 canvas數(shù)值運(yùn)算、圖形繪制與時(shí)間顯示相關(guān)操作技巧,需要的朋友可以參考下2019-05-05
使用JavaScript實(shí)現(xiàn)改造layer彈層移動(dòng)版組件
這篇文章主要為大家詳細(xì)介紹了使用JavaScript實(shí)現(xiàn)改造layer彈層移動(dòng)版組件的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-04-04

