JSONP跨域原理

170 阅读7分钟

1.同源策略

同源策略是对JavaScript代码能够操作哪些Web内容的一条完整的安全限制。当Web页面使用多个<iframe>元素或者打开其他浏览器窗口的时候,这一策略通常就会发挥作用。在这种情况下,同源策略负责管理窗口或窗体中的JavaScript代码以及和其他窗口或帧的交互。具体来说,脚本只能读取和所属文档来源相同的窗口和文档的属性。

脚本本身的来源和同源策略并不相关,相关的是脚本所嵌入的文档的来源,理解这一点很重要。例如,假设一个来自主机A的脚本被包含到(使用<script>标记的src属性)宿主B的一个Web页面中。这个脚本的来源是主机B,并且可以完整地访问包含它的文档的内容。

如果脚本打开一个新窗口并载入来自主机B的另一个文档,脚本对这个文档的内容也具有完全的访问权限。但是,如果脚本打开第三个窗口并载入一个来自主机C的文档(或者是来自主机A),同源策略就会发挥作用,阻止脚本访问这个文档。

2.聊聊json和jsonp的区别

JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官方跨域数据交互协议

JSON

JSON的优点:

1、基于纯文本,跨平台传递极其简单;

2、Javascript原生支持,后台语言几乎全部支持;

3、轻量级数据格式,占用字符数量极少,特别适合互联网传递;

4、可读性较强,虽然比不上XML那么一目了然,但在合理的依次缩进之后还是很容易识别的;

5、容易编写和解析,当然前提是你要知道数据结构;

JSON的缺点当然也有,但在作者看来实在是无关紧要的东西,所以不再单独说明。

JSON的格式或者叫规则:

JSON能够以非常简单的方式来描述数据结构,XML能做的它都能做,因此在跨平台方面两者完全不分伯仲。

1、JSON只有两种数据类型描述符,大括号{}和方括号[],其余英文冒号:是映射符,英文逗号,是分隔符,英文双引号""是定义符。

2、大括号{}用来描述一组“不同类型的无序键值对集合”(每个键值对可以理解为OOP的属性描述),方括号[]用来描述一组“相同类型的有序数据集合”(可对应OOP的数组)。

3、上述两种集合中若有多个子项,则通过英文逗号,进行分隔。

4、键值对以英文冒号:进行分隔,并且建议键名都加上英文双引号”",以便于不同语言的解析。

5、JSON内部常用数据类型无非就是字符串、数字、布尔、日期、null 这么几个,字符串必须用双引号引起来,其余的都不用,日期类型比较特殊,这里就不展开讲述了,只是建议如果客户端没有按日期排序功能需求的话,那么把日期时间直接作为字符串传递就好,可以省去很多麻烦。

JSONP

JSONP是一种跨域请求方式,其原理就是动态生成Script标签,设置src为远端地址,内容为一个js调用

3.JSONP跨域原理

主要是利用了<script>标签不受同源策略影响,不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<script>、<img>、<iframe>

先来实现JSONP的具体代码

function jsonp({url,params,callback}) {
  return new Promise((resolve,reject) => {
      let script = document.createElement("script") //创建script标签
      window[callback] = function(data) { //将后台拿到的数据用resolve“暴露”出去
          resolve(data)
          document.body.removeChild(script) //拿到数据之后应该删除该dom
      }
      params = {...params,callback} //将参数和callback函数装起来
      let arrs = []
      for(let key in params) {
          arrs.push(`${key}=${params.[key]}`) //辉形成形如 ["name=Mike","age=19","callback="show"]的一个数组
      }
      script.src = `${url}?${arrs.join('&')}`//将url连接起来
      document.body.appendChild(script) //插入dom
  })
}

//使用
jsonp({
  url:"http...",
  params: {wd: 'hehe'},
  callback: "show"
}).then(data => {
  console.log(data)
})

前端是如何拿到数据执行的呢?

  • 后端会用前端传过来的callback中的函数名包裹后台的数据
//后台代码
...
return "successCallback"+"("+json.dumps(memInfo)+")" #将结果以json形式返回,通过jsonp与前台交互
...
  • 其实就是相当于调用了前端传来的callback函数,例如例子就会变成
show({name: "amy", sex: woman})
  • 而在前端代码中,用resolve包裹了data数据,可以供then的链式调用取用,这样不就拿到后台数据了么
 window[callback] = function(data) { //将后台拿到的数据用resolve“暴露”出去
            resolve(data)
            document.body.removeChild(script) //拿到数据之后应该删除该dom
}
上面代码就相当于
show(data) {
    resolve(data)
}
  • 也就是相当于在前端中定义了一个函数,在服务端中调用并且传参进去,然后在前端中可以拿到参数作一些操作

注意

callback函数名不可以重复,根据上面的代码估计你也知道为什么了

4.JSONP的优缺点

1.优点

1.1它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制,JSONP可以跨越同源策略;

1.2它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持

1.3在请求完毕后可以通过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就相当于将controller层和view层终于分开了。我提供的jsonp服务只提供纯服务的数据,至于提供服务以 后的页面渲染和后续view操作都由调用者来自己定义就好了。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑就可以了,逻辑都可以使用同 一个jsonp服务。        

2.缺点

2.1它只支持GET请求而不支持POST等其它类型的HTTP请求

2.2它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

2.3 jsonp在调用失败的时候不会返回各种HTTP状态码。

2.4缺点是安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

5.JSONP跨域的script标签请求为什么不受同源策略的限制

这个问题的重点就在于 :要执行的脚本是如何判断他的来源的

脚本的来源取决于脚本所嵌入的资源的来源,比如说访问A主机的当前HTML文件中有一个script标签,这个script标签的src属性请求了一个js脚本,因为这个脚本是由A主机的HTML文件的嵌入的script标签发起请求获取的,因此这个脚本的来源是属于A主机的。

到了这里,问题的答案也就出来了,jsonp的script标签请求回来的资源与当前域是相同的域,因此不受同源策略的影响

对于同源策略这里还有一个很关键的点:同源策略并非应用于不同源的窗口中的所有对象的所有属性

参考文章: www.cnblogs.com/coding4/p/5… www.manongjc.com/detail/11-g… www.cnblogs.com/clphp/p/547…