跨域解决之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 相关文章推荐
在一个form用一个SUBMIT(或button)分别提交到两个处理表单页面的代码
Feb 15 Javascript
List Installed Software Features
Jun 11 Javascript
javascript instanceof,typeof的区别
Mar 24 Javascript
jquery keypress,keyup,onpropertychange键盘事件
Jun 25 Javascript
JavaScript字符串对象的concat方法实例(用于连接两个或多个字符串)
Oct 16 Javascript
jQuery实现按钮的点击 全选/反选 单选框/复选框 文本框 表单验证
Jun 25 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
Apr 19 Javascript
小程序封装wx.request请求并创建接口管理文件的实现
Apr 29 Javascript
vue项目初始化到登录login页面的示例
Oct 31 Javascript
原生JS实现微信通讯录
Jun 18 Javascript
JavaScript快速调试的两个技巧
Nov 04 Javascript
swiper实现导航滚动效果
Dec 13 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
Windows下的PHP5.0详解
2006/11/18 PHP
PHP网站基础优化方法小结
2008/09/29 PHP
你应该知道PHP浮点数知识
2015/05/13 PHP
PHP常用的小程序代码段
2015/11/14 PHP
php中的抽象方法和抽象类
2017/02/14 PHP
php分页查询mysql结果的base64处理方法示例
2017/05/18 PHP
php实现微信支付之退款功能
2018/05/30 PHP
PHP的PDO连接讲解
2019/01/24 PHP
Laravel服务容器绑定的几种方法总结
2020/06/14 PHP
capacityFixed 基于jquery的类似于新浪微博新消息提示的定位框
2011/05/24 Javascript
JavaScript中的细节分析
2012/06/30 Javascript
js DOM 元素ID就是全局变量
2012/09/20 Javascript
chrome浏览器不支持onmouseleave事件的解决技巧
2013/05/31 Javascript
javascript图片切换综合实例(循环切换、顺序切换)
2016/01/13 Javascript
JavaScript中boolean类型之三种情景实例代码
2016/11/21 Javascript
Vue.js实现模拟微信朋友圈开发demo
2017/04/20 Javascript
js防刷新的倒计时代码 js倒计时代码
2017/09/06 Javascript
JavaScript实现焦点进入文本框内关闭输入法的核心代码
2017/09/20 Javascript
Vue-Router实现组件间跳转的三种方法
2017/11/07 Javascript
mint-ui在vue中的使用示例
2018/04/05 Javascript
vant-ui AddressEdit地址编辑和van-area的用法说明
2020/11/03 Javascript
vue-calendar-component 封装多日期选择组件的实例代码
2020/12/04 Vue.js
[00:39]DOTA2上海特级锦标赛 Liquid战队宣传片
2016/03/04 DOTA
[02:23]2018DOTA2亚洲邀请赛趣味视频——反应测试
2018/04/04 DOTA
Python爬取三国演义的实现方法
2016/09/12 Python
为何人工智能(AI)首选Python?读完这篇文章你就知道了(推荐)
2019/04/06 Python
Python中的十大图像处理工具(小结)
2019/06/10 Python
解决jupyter运行pyqt代码内核重启的问题
2020/04/16 Python
Python figure参数及subplot子图绘制代码
2020/04/18 Python
美国学校用品、教室和教学商店:Discount School Supply
2018/04/04 全球购物
迪斯尼假期(欧洲、中东及非洲):Disney Holidays EMEA
2021/02/15 全球购物
大学生入党推荐书范文
2014/05/17 职场文书
放牛班的春天观后感
2015/06/01 职场文书
文艺部部长竞选稿
2015/11/21 职场文书
浅谈golang 中time.After释放的问题
2021/05/05 Golang
教你如何使用Python开发一个钉钉群应答机器人
2021/06/21 Python