对于javascript程序员来说,发送ajax请求获取后台数据然后把数据和模板拼接成字符串渲染回DOM实现无刷新更新页面这样的操作可谓是轻车熟路。但众所周知,ajax有一个不好,就是不能跨域传输数据,而跨域传输有时候又是必须用到的,比如我们可能需要调用第三方网站提供的某些API来获取某些信息,提供给我们网站的用户。
 
例如,要开发一个天气应用,你可能需要调用第三方的天气API,这个时候就必然涉及到跨域请求数据,因为毕竟我们不可能为了开发一个天气应用就自己搭建一个天气API。在少数情况下,如果第三方网站的服务器上设置了CORS机制,这时是可以直接用ajax发送请求的。但在大多数情况下,第三方网站都没有这么慷慨,因为涉及到安全问题,不可能允许任意人都能自由调用自己网站的接口。这个时候jsonp就应运而生了。
 
在HTML中有这样一类标签,它们都有一个src属性,src属性的值是一个链接,当标签一被解析到DOM中,就会开始把src属性中的链接指向的资源下载到本文档,例如,设置了src的img标签会自动从src属性的链接中下载资源,下载的资源又会被浏览器解析成图片,加到DOM中;设置了src属性的iframe元素会从src链接里下载一张网页;设置了src属性的script元素也会自动从src链接里下载javascript代码并执行,而这个过程中DOM本身也是没有刷新的,更令人心动的是,src属性的链接根本没有同域的限制。这个原理就是实现JSONP的基石。
 
但是,我们想用它来实现跨域还很有难度。这个src属性的元素的加载和一般的ajax请求有一个很大的不同,回想一下,在一个典型的ajax请求中,我们可以完全控制请求的过程,我们可以对指定的网页实行open,可以设置请求头,可以指定响应的MIMEtype,关键的是,我们可以从xhr对象的responseText属性中获取响应数据,然后拼接模板,渲染DOM。但在img, iframe, script这些标签中呢,我们的控制权就被剥夺了,我们设置src属性,浏览器负责发送请求,服务器端返回的响应直接就被加载回DOM了,我们根本没有插手修改数据的机会。
 
怎么办呢?这就像向遥远的深空发射一艘飞船,当飞船远离我们几十光年的时候,我们就不太可能从地面对它发送实时控制信号了,更好的做法就是把指令提前写在飞船上,让它自动执行。
 
和飞船的例子一样,一个典型的jsonp过程就是:创建一个script标签,设置src属性,这个src属性中包括了目标API的地址,我们的查询字符串querystring,querystring中最最重要的是我们的“指令”,因为script标签src返回之后,我们并不能控制返回的结果,所以最好让服务器返回的时候自己执行我们想要执行的操作。这个“指令”也就是jsonp中的“p”了。
 
举一个栗子:
1.我需要某个数据,比如就按照前面讲的,天气数据吧,于是我构造查询url。
var script = document.createElement('script');
script.src = 'www.weather.fake/get/?province=hubei&city=wuhan&callback=instruction';
document.getElementsByTagName('head')[0].appendChild(script);
//先写好指令,即回调
function instruction(data){
console.log(data);
}
//注意这里的instruction就是我们告诉服务器的“指令”。我们跟服务器说,我需要province为hubei,city为wuhan的地方的天气数据,但由于数据返回后我自己不能处理,所以你返回数据的时候自己处理好了,具体怎么处理,我已经写在名字叫做instruction的指令里面了。
2.weather.fake的服务器收到我的请求,从数据库里一找,找到了数据。一看,还有个指令,于是它就执行instruction指令。说起来很高级,实际上也就是把返回的数据包裹在instruction函数里面(jsonp的p,padding)。服务器于是返回这样一个东西:
//response.js
instruction({
  "city":"wuhan",
  "weather":"cloudy"
})
 
3.服务器端的writeheader设置和浏览器端的accept设置会保证返回的东西会被浏览器解释成一个js文件,于是我们事先写好的指令instruction函数就得到了执行,整个jsonp过程就完成了。
 
需要注意的几点:
 
1.返回的js文件是在全局作用域执行的,所以你要保证你写的回调函数instruction在全局作用域里。
 
 
2.这里的callback=instruction。其中,callback只是一个普通的querystring,是你和服务器事先约定的,不同API提供方,名字也不同,有的可能就叫cb,等等。至于instruction,你爱写什么写什么,但要保证和你写的回调处理函数名字一致。我这里为了方便理解,就写instruction了。
 
余论:ajax跨域,危险在哪里?
有些人说,禁止ajax跨域,是为了防止攻击者利用它向自己的服务器发送敏感信息(例如cookie等等),这显然是错误的。对于一个已经被注入攻击代码的网站,攻击者如果只是想向自己的服务器发送信息而已,就算不用ajax,也完全可以用img等标签直接向自己的服务器执行get请求发送数据,况且创建一个img标签可比写一个完整的ajax过程简单多了,根本用不着兴师动众,也就是说,单向GET请求ajax并没有优势。
 
为什么要限制ajax,因为它太强大了。看看CORS机制,它规定的是服务器方面的接受白名单。也就是说,由服务器来决定可以接受哪些客户可以向它请求数据。说明跨域限制主要是为了保证服务器端安全的。
 
一般能用src做到的,ajax都能做到,ajax能做到的src却不一定能够做到。例如src只能发起get请求,但ajax能发起任意类型的请求。
 
举个栗子,某博客网站的删除文章功能API可能必须要用DELETE方法发送请求才能执行,这个时候攻击者如果只用src构造请求就无能为力了,但ajax却可以轻易模拟用户动作。这个时候禁止跨域就显得很重要了。
 
具体过程:假设我的攻击网站是www.evil.com。而一个允许CORS跨域传输ajax的博客网站是www.blog.com。该博客网站中,当用户点击了删除按钮时,就会向服务器发送DELETE请求。具体为:www.blog.com/user/delete(?id=10000)(由于是DELETE方法,实际上querystring是不会附在url上的,这里为了便于说明而已。)这个时候,假如用户访问我的evil.com网站,而我在我网站的脚本里写ajax:
$.ajax({
url:'www.blog.com/user/delete',
method:'delete',
data:{id:10000},
})
这样是能成功的,因为当发送ajax时,会自动带上用户在www.blog.com的cookie,而请求方法又合法,所以完全能取得blog.com服务器的信任,也就能不知不觉地删除用户在blog.com上写的文章。

简单理解jsonp原理的更多相关文章

  1. 简单透彻理解JSONP原理及使用

    首先提一下JSON这个概念,JSON是一种轻量级的数据传输格式,被广泛应用于当前Web应用中.JSON格式数据的编码和解析基本在所有主流语言中都被实现,所以现在大部分前后端分离的架构都以JSON格式进 ...

  2. 深入理解JSONP原理——前端面试

    JSON和JSONP虽然只有一个字之差,但是它们俩是八竿子打不着的:JSON是一种数据交换格式,JSONP是非正式的跨域数据交换协议. 为什么说JSONP是非正式的传输协议呢?因为它就是利用了< ...

  3. 简单理解ThreadLocal原理和适用场景

    https://blog.csdn.net/qq_36632687/article/details/79551828?utm_source=blogkpcl2 参考文章: 正确理解ThreadLoca ...

  4. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  5. 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制

    [原创]分布式之数据库和缓存双写一致性方案解析(三)   正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...

  6. 简单的jsonp实现跨域原理

    什么原因使jsonp诞生?  传说,浏览器有一个很重要的安全限制,叫做"同源策略".同源是指,域名,协议,端口相同.举个例子,用一个浏览器分别打开了百度和谷歌页面,百度页面在执行脚 ...

  7. input屏蔽历史记录 ;function($,undefined) 前面的分号是什么用处 JSON 和 JSONP 两兄弟 document.body.scrollTop与document.documentElement.scrollTop兼容 URL中的# 网站性能优化 前端必知的ajax 简单理解同步与异步 那些年,我们被耍过的bug——has

    input屏蔽历史记录   设置input的扩展属性autocomplete 为off即可 ;function($,undefined) 前面的分号是什么用处   ;(function($){$.ex ...

  8. mDNS 原理的简单理解

    转自:http://www.binkery.com/post/318.html mDNS 原理的简单理解 mDNS multicast DNS , 使用5353端口. 在局域网内,你要通过一台主机和其 ...

  9. mDNS原理的简单理解——每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁,和我的IP地址是多少。然后其他也有该服务的主机就会响应,也会告诉你,它是谁,它的IP地址是多少

    MDNS协议介绍 mDNS multicast DNS , 使用5353端口,组播地址 224.0.0.251.在一个没有常规DNS服务器的小型网络内,可以使用mDNS来实现类似DNS的编程接口.包格 ...

随机推荐

  1. 字符串--P1553 数字反转(升级版)

    题目描述 给定一个数,请将该数各个位上数字反转得到一个新数. 这次与NOIp2011普及组第一题不同的是:这个数可以是小数,分数,百分数,整数.整数反转是将所有数位对调:小数反转是把整数部分的数反转, ...

  2. 使用 resultMap 实现高级结果集映射

    resultMap 的基本配置项 属性 id 属性:resultMap 的唯一标识,此 id 值用于 select 元素 resultMap 属性的引用. type 属性:表示该 resultMap ...

  3. 统计nginx日志里每五分钟的访问量

    #!/usr/bin/env python # -*- coding:utf-8 -*- # Author:Random_lee import time import os import re cla ...

  4. 51nod1128 正整数分组V2

    [题解] 二分一个最大值,check一下分出来的组数是否小于等于k即可. #include<cstdio> #include<algorithm> #define LL lon ...

  5. 阿里云服务器Ubuntu 16.04 3安装mysql

    .更新系统 apt-get update [注意:要在root用户下] .安装mysql-server apt-get install mysql-serverapt-get install mysq ...

  6. 使用Mybatis-Generator自动生成Dao、Model、Mapping相关文件(转)-----https://www.cnblogs.com/smileberry/p/4145872.html

    https://www.cnblogs.com/smileberry/p/4145872.html 使用Mybatis-Generator自动生成Dao.Model.Mapping相关文件(转)

  7. tyvj1271 零式求和

    描述 请考虑一个由1到N(N=3, 4, 5 ... 9)的数字组成的递增数列:1 2 3 ... N.现在请在数列中插入“+”表示加,或者“-”表示减,抑或是“ ”表示空白(例如1-2 3就等于1- ...

  8. sql sever 等待事件

    http://blog.csdn.net/dba_huangzj/article/details/7607844

  9. 问答:怎样规划CSS 中 的命名方式 怎样看待 CSS 中 BEM 的命名方式?

    好多盆友 非常纠结 css命名规则 怎么弄,还没起步就被绊住了.那么今天蝈蝈就针对这个问题来讨论一下 没什么技术 含量.但却对效率开发至关重要的 "问题". 下文是一些知乎大神的个 ...

  10. vijos- P1383盗窃-黑珍珠 (python + 代码优化)

    P1383盗窃-黑珍珠 Accepted 标签:怪盗基德 VS OIBH[显示标签] 背景 怪盗基德 VS OIBH 第二话 描写叙述 今次怪盗基德再次对阵OIBH,目标是Black Star!基德已 ...