跨域解决之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 相关文章推荐
JavaScipt中的Math.ceil() 、Math.floor() 、Math.round() 三个函数的理解
Apr 29 Javascript
Moment.js 不容错过的超棒Javascript日期处理类库
Apr 15 Javascript
Javascript动画的实现原理浅析
Mar 02 Javascript
跟我学习javascript的循环
Nov 18 Javascript
JavaScript 数组some()和filter()的用法及区别
May 20 Javascript
ECMAScript6轮播图实践知识总结
Aug 17 Javascript
javaScript 逻辑运算符使用技巧整理
May 03 Javascript
node内置调试方法总结
Feb 22 Javascript
Js面试算法详解
Apr 08 Javascript
详解在React中跨组件分发状态的三种方法
Aug 09 Javascript
深入浅析ng-bootstrap 组件集中 tabset 组件的实现分析
Jul 19 Javascript
js String.prototype.trim字符去前后空格的扩展
Aug 23 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数组函数序列之array_flip() 将数组键名与值对调
2011/11/07 PHP
详谈PHP文件目录基础操作
2014/11/11 PHP
ThinkPHP模版引擎之变量输出详解
2014/12/05 PHP
php支持中文字符串分割的函数
2015/05/28 PHP
再Docker中架设完整的WordPress站点全攻略
2015/07/29 PHP
Jquery从头学起第四讲 jquery入门教程
2010/08/01 Javascript
js完美的div拖拽实例代码
2014/01/22 Javascript
JS数组的常见用法实例
2015/02/10 Javascript
前端开发必知的15个jQuery小技巧
2017/01/22 Javascript
深入理解基于vue-cli的vuex配置
2017/07/24 Javascript
浅谈Angular路由复用策略
2017/10/04 Javascript
解决Vue 项目打包后favicon无法正常显示的问题
2018/09/01 Javascript
原生js实现3D轮播图
2020/03/21 Javascript
构建大型 Vue.js 项目的10条建议(小结)
2019/11/14 Javascript
JavaScript 实现HTML DOM增删改查操作的常见方法详解
2020/01/04 Javascript
Javascript摸拟自由落体与上抛运动原理与实现方法详解
2020/04/08 Javascript
详解Vue中Axios封装API接口的思路及方法
2020/10/10 Javascript
python通过邮件服务器端口发送邮件的方法
2015/04/30 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
python列表推导式操作解析
2019/11/26 Python
python实现图像拼接功能
2020/03/23 Python
Python 找出出现次数超过数组长度一半的元素实例
2020/05/11 Python
python numpy库np.percentile用法说明
2020/06/08 Python
一文轻松掌握python语言命名规范规则
2020/06/18 Python
浅谈python出错时traceback的解读
2020/07/15 Python
python exit出错原因整理
2020/08/31 Python
解决python 在for循环并且pop数组的时候会跳过某些元素的问题
2020/12/11 Python
纯css3实现思维导图样式示例
2018/11/01 HTML / CSS
世界最大的票务市场:viagogo
2017/02/16 全球购物
美国领先的家庭健康检测试剂盒提供商:LetsGetChecked
2019/03/18 全球购物
C/C++ 笔试、面试题目大汇总
2015/11/21 面试题
艺术节开幕词
2015/01/28 职场文书
满月酒邀请函
2015/01/30 职场文书
写作技巧:优秀文案必备的3种结构
2019/08/19 职场文书
Win11筛选键导致键盘失灵怎么解决? Win11关闭筛选键的技巧
2022/04/08 数码科技
Django数据库(SQlite)基本入门使用教程
2022/07/07 Python