跨域解决之JSONP和CORS的详细介绍


Posted in Javascript onNovember 21, 2018

JSONP跨域和CORS跨域

什么是跨域?

跨域:指的是浏览器不能执行其它网站的脚本,它是由浏览器的同源策略造成的,是浏览器的安全限制!

同源策略

同源策略:域名、协议、端口均相同。

浏览器执行JavaScript脚本时,会检查这个脚本属于那个页面,如果不是同源页面,就不会被执行。

JSONP跨域

只支持GET请求,不支持POST等其它请求,也不支持复杂请求,只支持简单请求。

CORS跨域

支持所有的请求,包含GET、POST、OPTOIN、PUT、DELETE等。既支持复杂请求,也支持简单请求。

JSONP和CORS跨域理解

使用目的: JSONP与CORS的使用目的相同,并且都需要服务端和客户端同时支持,但CORS的功能更加强大。
JSONP(json with padding 填充式json):利用了使用src引用静态资源时不受跨域限制的机制。主要在客户端搞一个回调做一些参数接收与操作的处理,并把这个回调函数告知服务器,而服务器端需要做的是按照JavaScript的语法把数据放到约定好的回调函数之中即可,jQuery很早之前就已经把JSONP语法糖化了,使用起来会更加方便。
CORS(Cross-origin resource sharing 跨域资源共享):依附于AJAX,通过添加HTTP Header部分字段请求与获取有权限访问的资源。CORS对开发者是透明的,因为浏览器会自动根据请求的情况(简单和复杂)做出不同的处理。CORS的关键是服务器端的配置支持,由于CORS是W3C中一项较“新”的方案,以至于各大网页解析引擎还没有对齐进行严格规格的实现,所以不同引擎下可能会有一些不一致。

JSONP和CORS的优缺点

1. JSONP的主要优势在于对浏览器的支持较好;虽然目前主流浏览器都支持CORS,但IE9及以下不支持CORS。

2. JSONP只能用于获取资源(即只读,类似于GET请求);CORS支持所有类型的HTTP请求,功能完善。(这点JSONP被玩虐,但大部分情况下GET已经能满足需求了)
JSONP的错误处理机制并不完善,我们没办法进行错误处理;而CORS可以通过onerror事件监听错误,并且浏览器控制台会看到报错信息,利于排查。

3. JSONP只会发一次请求;而对于复杂请求,CORS会发两次请求。

4. 始终觉得安全性这个东西是相对的,没有绝对的安全,也做不到绝对的安全。毕竟JSONP并不是跨域规范,它存在很明显的安全问题:callback参数注入和资源访问授权设置。CORS好歹也算是个跨域规范,在资源访问授权方面进行了限制(Access-Control-Allow-Origin),而且标准浏览器都做了安全限制,比如拒绝手动设置origin字段,相对来说是安全了一点。但是回过头来看一下,就算是不安全的JSONP,我们依然可以在服务端端进行一些权限的限制,服务端和客户端也都依然可以做一些注入的安全处理,哪怕被攻克,它也只能读一些东西。就算是比较安全的CORS,同样可以在服务端设置出现漏洞或者不在浏览器的跨域限制环境下进行攻击,而且它不仅可以读,还可以写。

应用场景

  • 如果需要兼容IE低版本浏览器,无疑,JSONP。
  • 如果需要对服务端资源进行操作,无疑,CORS。
  • 其他情况的话,根据自己的对需求的分析来决定和使用。

相同协议、域名、端口下

  • 页面在 http://localhost:3000/0
  • 服务在 http://localhost:3000/1
  • 控制台能正常输出 {name: '', sex: '', _stamp: ''}

views/0.ejs

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>

  </style>
</head>
<body>
  <form action="/" onsubmit="return false">
    <label for="name">NAME</label><input type="text" id="name" name="name"><br>
    <label for="sex">SEX</label><input type="text" id="sex" name="sex"><br>
    <input type="submit" value="SUBMIT">
  </form>
  <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
  <script>
    $('form').on('submit', function(){
      $.ajax({
        url: 'http://localhost:3000/1',
        data: {
          name: $('#name').val(),
          sex: $('#sex').val()
        },
        success: function(data){
          console.log(data);
        }
      })
    })
  </script>
</body>
</html>

serve.js

const server = require('express')();

server.set('view engine', 'ejs');

server.get('/0', (req, res) => {
  res.render('0.ejs', {});
})

server.get('/1', (req, res) => {
  let {name, sex} = req.query;
  res.send({name, sex, _stamp: + new Date});
})

server.listen(3000);

JSONP

把0.ejs改为0.hmtl,直接打开或者在http://localhost:3001/views/0.html打开(3000端口被占用时运行$ browser-sync start --server --files '**')

控制台报错 Access to XMLHttpRequest at 'http://localhost:3000/1?name=&sex=' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

jQuery 的 JSONP

0.html 的脚本改为

function jCb(data){ // 这个函数和ajax 的 success 同样可以操作 data
  console.log('你要的', data);
}
$('form').on('submit', function(){
  $.ajax({
    url: 'http://localhost:3000/1',
    data: {
      name: $('#name').val(),
      sex: $('#sex').val()
    },
    dataType: 'jsonp',
    jsonpCallback: 'jCb', // 服务端 req.query.callback = 'jCb'
    success: function(data){
      console.log(data);
    }
  })
})

server.js 注意返回客户端的字符串的拼接

const server = require('express')();
server.get('/1', (req, res) => {
  let {name, sex, callback} = req.query;
  var data = "{name: '', sex: '', _stamp: ''}"; // 然后再在单引号处拼接
    data = "{name: '" + name + "', sex: '" + sex + "', _stamp:' " + Date.now() + "'}";
    data = `{name: '${name}', sex: '${sex}', _stamp: '${+ new Date}'}`

  var resStr = callback + "()"
    resStr = callback + "(" + data + ")"
  console.log(callback, typeof callback); // jCb string
  console.log(resStr, typeof resStr); // jCb({name:'',sex:'',_stamp:'1542456915800'}) string
  res.send(resStr);
})

server.listen(3000);

利用 script 标签

0.html

<script>
  function jCb(data) {
    console.log("jsonpCallback: " + data.name)
  }
</script>
<script src = 'http://localhost:3000/1?jsonp=jsonpCallback'></script>
server.js

server.get('/1', (req, res) => {
  var data = 'var data = {name: $("#name").val(), sex: $("#sex").val(), _stamp: + new Date };'
  var debug = 'console.log(data);'
  var callback = '$("form").submit(function(){' + data + req.query.jsonp + '(data);' + debug + '});'
  res.send(callback);
})

CORS

html 代码还是最初的代码,server.js 改变

server.get('/1', (req, res) => {
  // 设置可以请求的域名,"*" 代表所有域名
  res.header("Access-Control-Allow-Origin", "http://localhost:3001");

  // 设置所允许的HTTP请求方法。
  res.header("Access-Control-Allow-Methods", "OPTIONS, GET, PUT, POST, DELETE");

  // 字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段。
  res.header("Access-Control-Allow-Headers", "x-requested-with, accept, origin, content-type");

  // 服务器收到请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

  // Content-Type表示具体请求中的媒体类型信息。
  res.header("Content-Type", "application/json;charset=utf-8");

  // 该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。
  // 当设置成允许请求携带cookie时,需要保证"Access-Control-Allow-Origin"是服务器有的域名,而不能是"*"。
  res.header("Access-Control-Allow-Credentials", true);

  // 该字段可选,用来指定本次预检请求的有效期,单位为秒。
  // 当请求方法是PUT或DELETE等特殊方法或者Content-Type字段的类型是application/json时,服务器会提前发送一次请求进行验证
  // 下面的的设置只本次验证的有效时间,即在该时间段内服务端可以不用进行验证
  res.header("Access-Control-Max-Age", 300);

  /*
  CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:
    Cache-Control、
    Content-Language、
    Content-Type、
    Expires、
    Last-Modified、
    Pragma。
  */
  // 需要获取其他字段时,使用Access-Control-Expose-Headers,
  // getResponseHeader('myData')可以返回我们所需的值
  res.header("Access-Control-Expose-Headers", "myData");


  let {name, sex} = req.query;
  res.send({name, sex, _stamp: + new Date});
})

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery插件开发方法(初学者)
Feb 03 Javascript
javascript动态加载三
Aug 22 Javascript
JS判断客户端是手机还是PC的2个代码
Apr 12 Javascript
JSONP原理及简单实现
Jun 08 Javascript
Wireshark基本介绍和学习TCP三次握手
Aug 15 Javascript
jQuery查找节点方法完整实例
Sep 13 Javascript
js 中获取制定的cook信息实现方法
Nov 19 Javascript
详解Js模板引擎(TrimPath)
Nov 22 Javascript
Vue.js之slot深度复制详解
Mar 10 Javascript
VueJs组件之父子通讯的方式
May 06 Javascript
解析JS在获取当前月的最后一天遇到的坑
Aug 30 Javascript
vue+animation实现翻页动画
Jun 29 Javascript
如何去除富文本中的html标签及vue、react、微信小程序中的过滤器
Nov 21 #Javascript
JSON生成Form表单的方法示例
Nov 21 #Javascript
apicloud拉起小程序并传递参数的方法示例
Nov 21 #Javascript
vue中组件的过渡动画及实现代码
Nov 21 #Javascript
详解promise.then,process.nextTick, setTimeout 以及 setImmediate的执行顺序
Nov 21 #Javascript
Nuxt.js之自动路由原理的实现方法
Nov 21 #Javascript
nuxt.js中间件实现拦截权限判断的方法
Nov 21 #Javascript
You might like
php自动获取字符串编码函数mb_detect_encoding
2011/05/31 PHP
php返回相对时间(如:20分钟前,3天前)的方法
2015/04/14 PHP
PHP中利用sleep函数实现定时执行功能实现代码
2016/08/25 PHP
php arsort 数组降序排序详细介绍
2016/11/17 PHP
关于PHP5.6+版本“No input file specified”问题的解决
2019/12/11 PHP
PHP SESSION跨页面传递失败解决方案
2020/12/11 PHP
JS加ASP二级域名转向的代码
2007/05/17 Javascript
js中prototype用法详细介绍
2013/11/14 Javascript
js控制表单不能输入空格的小例子
2013/11/20 Javascript
jquery改变disabled的boolean状态的三种方法
2013/12/13 Javascript
JavaScript语言对Unicode字符集的支持详解
2014/12/30 Javascript
用js判断是否为360浏览器的实现代码
2015/01/15 Javascript
nodejs实现遍历文件夹并统计文件大小
2015/05/28 NodeJs
json对象转为字符串,当做参数传递时加密解密的实现方法
2016/06/29 Javascript
基于JavaScript实现轮播图代码
2016/07/14 Javascript
JavaScript中Array的实用操作技巧分享
2016/09/11 Javascript
微信小程序 配置文件详细介绍
2016/12/14 Javascript
如何在vue里添加好看的lottie动画
2018/08/02 Javascript
浅谈element中InfiniteScroll按需引入的一点注意事项
2020/06/05 Javascript
基于react项目打包css引用路径错误解决方案
2020/10/28 Javascript
js调用网络摄像头的方法
2020/12/05 Javascript
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
[01:01:52]DOTA2-DPC中国联赛定级赛 SAG vs iG BO3第二场 1月9日
2021/03/11 DOTA
基于Python Numpy的数组array和矩阵matrix详解
2018/04/04 Python
Windows下Pycharm远程连接虚拟机中Centos下的Python环境(图文教程详解)
2020/03/19 Python
Jupyter Notebook折叠输出的内容实例
2020/04/22 Python
在keras下实现多个模型的融合方式
2020/05/23 Python
python相对企业语言优势在哪
2020/06/12 Python
英国领先的家庭时尚品牌:Peacocks
2018/01/11 全球购物
Armor Lux法国官方网站:水手服装、成衣和内衣
2020/05/26 全球购物
火山动力Java笔试题
2014/06/26 面试题
Java程序员面试题
2013/07/15 面试题
学生会竞选自荐信
2013/10/12 职场文书
建筑管理专业求职信
2014/07/28 职场文书
2014最新预备党员思想汇报范文:中国梦,我的梦
2014/10/25 职场文书
专家推荐信范文
2015/03/26 职场文书