前言

今天,无意间看到自己某个文件夹下有个JSONP的东西。慢慢回忆起,这个东西是之前想写的一个demo,也不知道是多久以前了,但是不知道怎么的,给忘那边了。那么,就趁这个机会把它完成吧,其实也说不上是一个demo,就是一个小实验,虽然,网上也已经有很多关于JSONP的文章和例子了,但是有些东西看看很简单,不亲自试一下总觉得不踏实。我今天为什么要实验,一方面也是经常在网上看到有些网站需要跨域获得数据,但是目前自己做的项目中又没有相关需求,于是很好奇,于是就有了这篇文章,于是......那就开始这次练习吧。

一 、什么是JSONP

JSONP全称:JSON with Padding

看到名字,好像说,JSONP是JSON的什么?或许有人会问,什么是JSON呢?如果有同学还不清楚JSON,可以先去了解下JSON,然后再继续本文的阅读或许会更好。简单的说,JSON是一种数据交换格式,在我们通过ajax技术获取数据的时候,可以以XML或者JSON这样的格式进行传递。ajax虽然好用,但是也有遇到困难的时候,比如你需要跨域获取数据。这个时候,普通的ajax获取方式就不太容易了,这时候,JSONP就可以帮忙了。这里再补充下前面提到跨域问题,跨域其实简单的说就是,比如你自己写了一个网站把它部署到域名是www.a.com的服务器上,然后你可以毫无压力的使用ajax请求www.a.com/users.json  的数据。 但是,当要你通过普通ajax方式请求www.b.com域名下的www.b.com/users.json的数据时,就没那么容易了,在后面的小实验中,可以看到这一情况。既然使用普通的ajax技术无法做到,那么JSONP又是如何做到的呢?

二 、JSONP的基本原理

JSONP可以实现跨域,这要归功于强大的<script></script>元素标签。除了我们会在它中间写js代码外,也经常会在网站中通过它的src属性引入外部js文件,关键就在此,我们的引入的js文件也可以不是同一个域下的。那么我们也就可以将原来需要获取的JSON数据写到js文件中去,然后再获取。不过,不幸的事情终究发生了,当我们把一段JSON格式的数据,例如:

{"id" : "1","name" : "小王"}

写入js文件,然后通过<script>元素引入后,却报错了。原因是<script>标签元素还是很老实的,因为它就是负责执行js的,所以你那个JSON格式的数据它也会毫不犹豫的当作js代码去执行,而那个数据根本不符合js语法,于是就很悲剧的出错了。但这个出错,同样却带给了我们答案,不是吗?既然不符合js语法不行,我们搞个符合的不就可以了。这里一种常用的办法就是返回一个函数callback({"id" : "1","name" : "小王"}); 的执行语句就可以了。这里的callback命名不是必须的,你可以换任何喜欢的名字。这里只是强调这是个回调函数才这么写。回调函数确实强大啊,要使得这里可以执行该函数,那么这个函数必须在开始就已经被我们提前定义了。我们在开始就定义好:

function callback(data){alert(data.name);
}

其实这个不难理解,普通的函数执行或许大家都明白,在<script>标签中间先定义上面的函数,但是该函数并不会运行,因为你没执行调用,当你接着在代码中写上
callback({"id" : "1","name" : "小王"});就顺利的执行了。而JSONP所做的就是这个事情,只不过调用的语句从远程服务器传来,动态加入到你的页面中去执行而已。到这里只剩下最后一步了,就是告诉服务器端返回哪个名称的函数执行,这个也好办,将函数名以一个查询参数传递到后台告诉它名字就好了,类似:

http://www.b.com/getUsers.json?callback=getUsers

然后在服务器端处理,获得参数callback的值,然后将数据填充到getUser(data);的函数参数中去,这里的data。返回前台页面后,便可以执行并获得data数据了。到此,也终于明白了JSON with Padding中的Padding(填充)了。关于JSONP的基础理论部分就结束了,剩下的内容就剩下实验部分了。

三 、JSONP小实验

  • 实验环境:windows操作系统。
  • 开发工具:NotePad++。
  • 开发语言:Node.js。
  • 前台使用插件:jQuery。

开始了,这里选择Node.js,没其它原因,我只是顺手抓到它了,你当然也可以用asp.net,java servlet,php,ruby,Golang等等等你喜欢的去实验。因为只实验JSONP,没多少东西,所以Node.js中也没有使用第三方的框架(不过后来有点后悔了,多写了好多......)。

首先需要模拟两个域,因为我在windows下,所以可以修改host文件,添加

两个域名映射到本机回送地址127.0.0.1。然后开始写代码:
创建两个Node.js的应用,一个是appA.js,一个appB.js。首先,我们尝试通过普通ajax获取同域的数据:
appB.js代码:

以上截图是ajax请求数据部分。我们打开浏览器,输入地址后,如下:

这里有个按钮获取我的粉丝,ajax就从url:"http://www.b.com:9099/followers.json该源获得数据,这个数据在代码中,我们也可以找到,就是

当点击获取后,如下:

成功,没问题,我们再复制一份一样的代码,另存为appA.js,然后修改listen端口:

修改appB.js中ajax请求的URL为http://www.a.com:8088/followers.json ,现在是appB服务器本身是http://www.b.com:9099/index 而去请求www.a.com下的数据===》启动它

然后点击获取粉丝按钮会发现:

真的没有取到数据。。。。。。

再试试JSONP的方式,我们修改appB.js如下:

var http = require('http'),url = require('url'),fs  = require('fs'),path = require('path');function getFile(localPath,mimeType,res){fs.readFile(localPath,function(err,contents){if(!err){res.writeHead(200,{'Content-Type' : mimeType,'Content-length' : contents.length});res.end(contents);}else{res.writeHead(500);res.end();}});
}
http.createServer(function(req,res){var urlPath = url.parse(req.url).pathname;var fileName = path.basename(req.url) || 'index.html',suffix  = path.extname(fileName).substring(1),dir = path.dirname(req.url).substring(1),localPath = __dirname + '\\';if(suffix === 'js'){localPath += (dir ? dir + '\\' : '') + fileName;path.exists(localPath,function(exists){if(exists){getFile(localPath,'js',res);}else{res.writeHead(404);res.end();}});}else{if(urlPath === '/index'){res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});var html = '<!DOCTYPE html>'+'<head>'+'<script type="text/javascript">var getFollowers= function(data){alert(decodeURIComponent(data.users[0].name));};</script>'+'<script type="text/javascript" src="jquery.js"></script>'+'<script type="text/javascript" src="http://www.a.com:8088/index?callback=getFollowers"></script>'+'<script>'+'$(function(){'+  ''+  '$("#getFo").click(function(){'+'    $.ajax({'+            'url:"http://www.a.com:8088/followers.json",'+       'type:"get",'+       'success:function(json){'+       '    alert(json.users[0].name);'+       '}'+        '});'+     ''+    '});'+'});'+'</script>'+'</head>'+'<body>'+'<h1>hello i am server b </h1>'+'<input id="getFo" type="button" value="获取我的粉丝"/>'+'</body>'+'</html>';res.write(html);res.end();}else if(urlPath === '/followers.json'){res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});var followers = {"users" : [{"id" : "1","name" : "小王"},{"id" : "2","name" : "小李"}]};var fjson = JSON.stringify(followers);res.end(fjson);}else{res.writeHead(404,{'Content-Type':'text/html;charset=utf-8'});res.end('page not found');}}}).listen(9099);
console.log('Listening app B at 9099...');

注意看48行和51行,48行定义了回调函数,51行通过<script>标签,请求不同域的数据,其中传递参数callback=getFollowers

然后修改appA.js如下:

var http = require('http'),url = require('url'),querystring = require('querystring');http.createServer(function(req,res){var path = url.parse(req.url).pathname;var qs = querystring.parse(req.url.split('?')[1]),json;if(qs.callback){var followers = {users : [{id:'1',name:encodeURIComponent('小王')}]};var fjson = JSON.stringify(followers);console.log(fjson);json = qs.callback + "(" + fjson + ");";res.writeHead(200,{'Content-Type':'application/json','Content-Length' : json.length});res.end(json);}if(path === '/index'){res.writeHead(200,{'Content-Type':'text/html;charset=utf-8'});res.end('home');}else if(path === '/followers.json'){res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});var followers = {"users" : [{"id" : "1","name" : "小王"},{"id" : "2","name" : "小李"}]};var fjson = JSON.stringify(followers);res.end(fjson);}else{res.writeHead(404,{'Content-Type':'text/html;charset=utf-8'});res.end('page not found');}res.end('hello');
}).listen(8088);
console.log('Listening app A at 8088...');

第10行-23行,我们处理了传递的参数,并将数据填充到函数参数,并发送到请求者那边。再次运行两个程序,刷新http://www.b.com:9099/index便直接得到a域下的数据了,似乎成功了。但是,我不想马上执行呀,我也要和前面一样,点击按钮再获得,怎么办?这个也简单。只需要当我们点击的时候动态的引入<script>就可以了,修改click事件处理部分的代码:

$("#getFo").click(function(){
$("<script><//script>").attr("src","http://www.a.com:8088/index?callback=getFollowers").appendTo("body");
});

再次重启服务器,当点击按钮就可以获取数据了。接下来,我们再看看jQuery又是如何处理JSONP的呢?

四 、jQuery中处理JSONP

要通过jQuery使用JSONP是非常方便的,只需要修改最开始的ajax部分代码如下:

$.ajax({url:"http://www.a.com:8088/index",dataType:"jsonp",jsonp:"callback",type:"get",success:function(json){alert(decodeURIComponent(json.users[0].name));}
});

其中,jsonp指明了querystring的key为callback,value如果不指定,jQuery会默认随机生成一个名称:

var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( ajax_nonce++ ) );

五 、JSONP可能引起的安全性问题

由JSONP可能引起的安全问题主要是可能会遭受CSRF/XSRF的攻击,而使得容易遭受该攻击的也恰恰是上文中一直提到的JSONP的特点--可以跨源访问资源。普通的CSRF/XSRF攻击,仅仅可能利用受攻击用户,骗取服务器的信任,这样就可以模拟受攻击者对服务器进行一些有危害的请求,例如修改受攻击者的个人信息。但是,由于浏览器同源策略的限制,在第三方“恶意网站”无法读取服务器返回的信息。也就是说,攻击者只能捣捣乱,但是他还是获取不到受攻击者的敏感信息的(无XSS注入的前提下)。但是,如果服务器上某个请求使用了JSONP返回用户数据,可想而知,在第三方,或者任何方网站都能顺利的获取到。关于CSRF/XSRF攻击,就说到这里,具体实现方式就不展开了。

除了CSRF/XSRF攻击外,另外使用JSONP的网站(相对于部署JSONP的服务器)也可能有安全性问题。 因为,通过上面的实验,我们看到了,通过JSONP请求远程服务器后,返回的是一个在本网站立即执行的函数。相当于这个脚本直接被注入到当前页面了。如果远端网站中存在注入漏洞,那么后果可想而知了。为了防止这样的事情发生,可以使用 JSON-P 严格安全子集使浏览器可以对 MIME 类别是“application/json-p”请求做强制处理。如果回应不能被解析为严格的 JSON-P,浏览器可以丢出一个错误或忽略整个回应。关于安全性的问题先说到这里,安全问题永远是一个矛与盾的问题,总之,在互联网上,没有绝对的安全。如果再展开下去又会引出一堆东西,所以今天就先不说了。至于如何防范JSONP容易受到的CSRF/XSRF攻击,笔者认为最简单有效的方法就是对于敏感信息不要使用JSONP,因为也没有实际遇到过,不知道什么更好的解决方案。今天就到这里了,希望对大家有用~

【JSONP技巧拓展】————4、一次关于JSONP的小实验与总结相关推荐

  1. 【JSONP技巧拓展】————2、JSONP 安全攻防技术

    关于 JSONP JSONP 全称是 JSON with Padding ,是基于 JSON 格式的为解决跨域请求资源而产生的解决方案.他实现的基本原理是利用了 HTML 里 <script&g ...

  2. 【技巧总结】——关于不知道放到哪里的小技巧

    [技巧总结]--关于不知道放到哪里的小技巧 折半搜索 分治 0,1,-1转化 模型转换 [ExtendingSetofPointsExtending\;Set\;of\;PointsExtending ...

  3. jsonp原理详解——终于搞清楚jsonp是啥了,rabbitmq原理总结

    5.这样,解决方案就呼之欲出了,web服务端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件,显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装进去. ...

  4. jsonp解决ajax跨域问题,用JSONP解决ajax跨域问题

    JSONP:JSON With Padding 要点: 1.script标签 2.用script标签加载资源是没有跨域问题的 概要: 在资源加载进来之前先定义一个函数,这个函数接受一个参数(数据),函 ...

  5. JS的jsonp是什么?5分钟学会jsonp跨域请求

    一.jsonp是什么? jsonp是解决跨域请求的一种技术.浏览器为了防止CSRF攻击会采用同源策略(协议/主机/端口均相同)限制,对非同源发起http请求(即跨域请求)会被浏览器阻止. 二.json ...

  6. java jquery jsonp 跨域_Jquery跨域调用(JSONP)遇到error问题的解决

    之前Jquery的跨域调用一直没有解决,不知道为什么老是执行error里的语句,今天花了点时间研究了一下,终于把问题解决了. 关键的地方是返回的字符串,返回的字符串必须包含jsonp的回调函数名称,而 ...

  7. 【XSS技巧拓展】————4、浅谈跨站脚本攻击与防御

    跨站脚本简称xss(cross-site scripting),利用方式主要是借助网站本身设计不严谨,导致执行用户提交的恶意js脚本,对网站自身造成危害.xss漏洞是web渗透测试中最常见而又使用最灵 ...

  8. 【RPO技巧拓展】————3、IIS和.Net应用程序中的非根相对路径覆盖(RPO)

    在这篇博文中,我将向您展示一种更好的方法来利用ASP.NET Web窗体应用程序中的非根相对路径覆盖问题.这是一个低风险漏洞,可用于将资源(如样式表)甚至动态JavaScript注入受影响的网页. 介 ...

  9. 【PHP渗透技巧拓展】————3、LFI、RFI、PHP封装协议安全问题学习

    本文希望分享一些本地文件包含.远程文件包含.PHP的封装协议(伪协议)中可能包含的漏洞 目录 1. 文件包含的基本概念 2. LFI(Local File Include) 3. RFI(Remote ...

最新文章

  1. 利益驱动 需求驱动 技术驱动 谁才是真正的驱动力?
  2. webpack 项目使用webpack-dev-server 自动编译 (2)
  3. 手撕设计模式之「工厂方法模式」(Java描述)
  4. 北大poj1018题解题报告
  5. python 测试字符串类型_【教程】如何用Python中的chardet去检测字符编码类型
  6. hadoop自定义key,value
  7. Vue项目中Table设置 render 函数
  8. 用Crossdev安装MIPS交叉编译工具链
  9. python xml字符串_python -解析字符串,并返回xml格式字符串 急该如何解决
  10. 返回json格式报错:Inf and NaN cannot be JSON encoded
  11. Python 批量推送微信公众号模板消息
  12. 论文写作,word首页脚注不显示编号
  13. jQuery制作手风琴图片切换效果
  14. 小图标的使用(插入icon图标)
  15. java39阶台阶_第四届蓝桥杯javaC组_第39级台阶
  16. 诚聘.NET高级软件工程师
  17. 逻辑运算符,与,或,非,以及优先级。
  18. linux 桌面小部件,[试玩] FMXLinux (Firemonkey for Linux) Linux 桌面开发(第三方插件)...
  19. Blue Prism机器人自动化的20个最吸引人的功能
  20. 软件工程是不是教会不怎么会写程序的人开发软件?说说你的观点。

热门文章

  1. Oracle中lead() over()的用法
  2. 业绩归因Brinson模型
  3. 【哔哩哔哩播放器】2020最新版高仿哔哩哔哩视频播放器网站源码
  4. OWIN - Open Web Interface for .NET
  5. 腾讯回应微信刷掌支付;iPhone 13 Pro或提供1TB版本;Git 2.33 发布|极客头条
  6. bzoj 1296 洛谷4158 [SCOI2009]粉刷匠 题解
  7. tracker启动立马关闭,一直尝试重启
  8. 美工做什么副业?兼职美工如何赚钱?
  9. 门店系统有哪些功能,应该如何选择门店管理系统?
  10. html书签导入苹果,如何将Safari书签迁移到Win10 Web浏览器