一、了解跨域

👉何为跨域

  • 跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截

那为什么拦截呢,我们应该先了解一下什么是同源策略

  • MDN上的解释:同源策略是一个重要的安全策略,它用于限制一个origin的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介

具体的限制(三限制):

  • 同源策略限制了来自不同源的 JavaScript 脚本对当前DOM对象读和写的操作
  • 同源策略限制了来自不同源的站点读取当前的 Cookie、LocalStorage 等数据
  • 同源策略限制了通过 XMLHttpRequest等方式将站点的数据发送给不同源的站点

能使的标签(能跨域请求资源的标签):

  • <script src="..."></script>
  • <link rel="stylesheet" href="...">
  • 通过 <img>展示图片
  • 通过 <video>和 <audio>播放的多媒体资源
  • 通过 <iframe>载入的任何资源

那你限制了我这么多东西,我要正常使用肯定要跨域喽

👉何为同源

如果两个 URL 的协议(Protocol),域名(domain),端口(port)都相同的话,则这两个 URL 是同源

  • 协议:定义了数据如何在计算机内和之间进行交换的规则的系统。例如:http,https
  • 域名:可以通过DNS解析为IP地址 (这里即使两个不同的域名指向同一个IP地址,也是属于不同源的)
  • 端口:http默认使用80端口,https默认使用443端口

举个例子: 我们寻找跟掘金首页:https://jbzj.cn/同源的URL

URL 结果 原因
http://jbzj.cn/ 不同源 协议不同
https://jbzj.my.cn/ 不同源 域名不同
https://jbzj.cn:3030/ 不同源 端口不一样(https默认443端口)
https://jbzj.cn/user/2375390426313406 同源 只有路径不一样
https://jbzj.cn/post/7151609034699718692 同源 只有路径不一样

小结:跨域的出现是因为浏览器的同源策略对不同协议,域名,端口的URL在DOM对象的读写,Cookie等数据的获取,Ajax请求等方面有限制

二、跨域解决方案

JSONP(JSON with Padding)

  • 最容易记住也最容易理解的应该就是JSONP了
  • 我们前面提到了<script src="..."></script>是可以跨域请求资源的,那么JSONP就是利用了这一点了

🤜简单实现

  • 先上一个简单的例子来理解思路
  • 在客户端
  <script>
    function jsonp(){
      return new Promise((resolve, reject) => {
      //在全局定义一个doSomething的函数
        window['doSomething'] = function(data) {
          resolve(data); 
        }
      })
    }
    jsonp().then(res=>{
    //调用doSomething之后会在这里打印
      console.log(res)
    })
  </script>
  //使用script标签向后台/test路径发送参数callback为doSomething
  <script src="http://localhost:3000/test?callback=doSomething"></script>
  • 在服务端
app.get('/test',(req, res) => {
    //通过res.send()方法-将处理好的内容,发送给客户端
    let { callback } = req.query;
    //返回一个函数的执行式doSomething(‘参数')
    res.send(`${callback}('这里是某车在使用简单的JSONP')`)
})
  • 服务器返回doSomething('这里是某车在使用简单的JSONP'),因为我们已经声明过,所以客户端会直接调用doSomething这个函数,然后打印出了:这里是某车在使用简单的JSONP

🤜流程图

  • 全局声明一个回调函数
  • 在<script>标签中,输入URL,向服务器传递该函数名和参数(可以通过问号传参:?callback=doSomething)
  • 服务器接收到请求后,需要进行处理:把传递进来的函数名和要返回的数据拼接为函数调用式,例如:传递进去的函数名是doSomething,要传回的数据是:这里是某车在使用简单的JSONP,那么后台返回的即为doSomething('这里是某车在使用简单的JSONP')
  • 客户端再调用执行之前声明的回调函数(doSomething),对返回的数据进行操作

🤜封装

但是在真正的开发中,我们不可能写一堆<script>标签在那里,所以我们需要对jsonp进行封装

    function jsonp({url, params, callback}){
      return new Promise((resolve, reject) => {
      //动态创建script标签
        let script = document.createElement('script');
        //处理传入的参数
        params = {...params, callback};
         //转换参数表达式
        let arr = []
        for(let key in params) {
          arr.push(`${key}=${params[key]}`)
        }
        //在路径中,参数用 & 隔开
        script.src = `${url}?${arr.join('&')}`
        //添加 script 标签
        document.body.appendChild(script);
        //声明回调函数
        window[callback] = function(data) {
        //执行异步函数
          resolve(data);
          //请求完后移除该script标签
          document.body.removeChild(script)
        }
      })
    }
  • 使用
   jsonp({
      url:'http://localhost:3000/test',
      params:{ args:'这个是参数' },
      callback: 'doSomething'
    }).then(res=>{
      console.log(res) 
    })

🤜优缺点

  • 优点: 简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题
  • 缺点: 仅支持get方法具有局限性,不安全可能会遭受XSS攻击

CORS(Cross-Origin Resource Sharing)

  • 实现CORS的关键在于后端:服务端设置Access-Control-Allow-Origin就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源
  • 我们可以将请求分类为简单请求和非简单请求

简单请求

判定:

请求方法是以下方三种方法之一

  • HEAD
  • GET
  • POST

请求头仅包含安全的字段,如以下几种字段

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain

请求过程

  • 客户端代码
    btn.addEventListener('click',()=>{
      const xhr = new XMLHttpRequest();
      xhr.open('get', 'http://localhost:3000/test'); //向端口号为3000的发起请求
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState == 4) {
          if(xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.response) //如果请求成功,则打印后台返回的数据
          }
        }
      }
    })
  • 服务端设置
app.get('/test',(req,res)=>{
    //通过res.send()方法-将处理好的内容,发送给客户
    res.send(`你好呀某车,你已经成功发起请求了`)
})
  • 当你发送请求的时候(客户端为5500端口,服务端为3000端口),什么也不设置就会报跨域错误

  • 此时我们看一下当浏览器会给它的请求头加上Origin,值为它本身的协议 域名 端口,这个时候服务器就能知道是谁在对他发送请求

  • 那么服务器如果想要允许它访问的话,则在响应头加上Access-Control-Allow-Origin字段,值可以为*表示允许所有源的访问,也可以为具体的源,我们这里给他设置为我们上述的Origin值,同时也加上Access-Control-Allow-Methods字段,设置可访问的请求方法
app.all("*",function(req,res,next){
    //设置允许跨域的域名,*代表允许任意域名跨域
    res.header("Access-Control-Allow-Origin","http://127.0.0.1:5500");
    res.header("Access-Control-Allow-Methods",'PUT,POST,GET,DELETE,OPTIONS')
    next();
})
  • 此时你再点击发送请求就会发现:请求已经能够正常返回了

  • 响应头显示的Access-Control-Allow-Origin即为我们可访问的源

复杂请求

  • 我们上述已经讲过什么是简单请求了,那么不符合上述条件的即为复杂请求
  • 复杂请求发起请求的时候则是会在正式通信之前进行一次预检请求(preflight request)
  • 浏览器先询问服务器,当前源当前请求是否可以访问服务器资源,只有得到正确的答复,才会进行正式的请求

请求过程

我们依旧使用上面的代码,然后就请求方式改为put,此时发送的即为复杂请求,可以看到发送了两次请求,由于我们put方法在上述已经被设置为可访问的方法,所以现在预检可以通过,我们也能正常请求到资源

如果我们将put方法从Access-Cntrol-Allow-Methods移除,此时再进行请求,则发提示,在预检请求中发送PUT方法是不被允许的

此时test正式请求则报CORS错误

写到这里,我就想到了曾经看过的面试题:跨域请求如何携带cookie

扩展

  • 但是我想测试的时候受到了SameSite的限制,不允许第三方cookie,本来想要关掉浏览器设置的,搜索了一下发现这个方法自从版本91之后就用不了,那么浅浅说一下方法就得了
  • 只需要前端在发送Ajax请求的时候将withCredentials设置为true
    const xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
  • 后端设置"Access-Control-Allow-Credentials为true,同时注意Access-Control-Allow-Origin值不为*
    res.header("Access-Control-Allow-Credentials", "true");

缺点

IE10以下不支持

小结:后端是关键,主要是设置Access-Control-Allow-Origin

关于postMessage和webSocket的跨域方案我写在嗦嗦postMessage和webSocket这里了

以上就是简单易懂的JSONP和CORS跨域方案详解的详细内容,更多关于JSONP CORS跨域方案的资料请关注Devmax其它相关文章!

简单易懂的JSONP和CORS跨域方案详解的更多相关文章

  1. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  2. 简单易懂的JSONP和CORS跨域方案详解

    这篇文章主要为大家介绍了简单易懂的JSONP和CORS跨域方案详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  3. Ajax跨域问题的解决办法汇总(推荐)

    本文给大家分享多种方法解决Ajax跨域问题,非常不错具有参考借鉴价值,感兴趣的朋友一起学习吧

  4. vue代理模式解决跨域详解

    这篇文章主要介绍了vue代理模式解决跨域详解的相关资料,需要的朋友可以参考下

  5. Vue如何解决跨域问题详解

    VUE访问接口的时候,很可能出现跨域请求,从而被提供接口的服务器拒绝,下面这篇文章主要给大家介绍了关于Vue如何解决跨域问题的相关资料,需要的朋友可以参考下

  6. Laravel开启跨域请求的方法

    今天小编就为大家分享一篇Laravel开启跨域请求的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  7. vue中this.$http.post()跨域和请求参数丢失的解决

    这篇文章主要介绍了vue中this.$http.post()跨域和请求参数丢失的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  8. vue中前端代理跨域问题实例总结

    前后端分离进行项目开发,跨域问题不可避免,下面这篇文章主要给大家介绍了关于vue中前端代理跨域问题的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

  9. 解决Django cors跨域问题

    这篇文章主要介绍了解决Django cors跨域问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  10. SpringBoot整合Web之CORS支持与配置类和 XML配置及注册拦截器

    这篇文章主要介绍了SpringBoot整合Web开发中CORS支持与配置类和 XML配置及注册拦截器的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

随机推荐

  1. js中‘!.’是什么意思

  2. Vue如何指定不编译的文件夹和favicon.ico

    这篇文章主要介绍了Vue如何指定不编译的文件夹和favicon.ico,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

  3. 基于JavaScript编写一个图片转PDF转换器

    本文为大家介绍了一个简单的 JavaScript 项目,可以将图片转换为 PDF 文件。你可以从本地选择任何一张图片,只需点击一下即可将其转换为 PDF 文件,感兴趣的可以动手尝试一下

  4. jquery点赞功能实现代码 点个赞吧!

    点赞功能很多地方都会出现,如何实现爱心点赞功能,这篇文章主要为大家详细介绍了jquery点赞功能实现代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  5. AngularJs上传前预览图片的实例代码

    使用AngularJs进行开发,在项目中,经常会遇到上传图片后,需在一旁预览图片内容,怎么实现这样的功能呢?今天小编给大家分享AugularJs上传前预览图片的实现代码,需要的朋友参考下吧

  6. JavaScript面向对象编程入门教程

    这篇文章主要介绍了JavaScript面向对象编程的相关概念,例如类、对象、属性、方法等面向对象的术语,并以实例讲解各种术语的使用,非常好的一篇面向对象入门教程,其它语言也可以参考哦

  7. jQuery中的通配符选择器使用总结

    通配符在控制input标签时相当好用,这里简单进行了jQuery中的通配符选择器使用总结,需要的朋友可以参考下

  8. javascript 动态调整图片尺寸实现代码

    在自己的网站上更新文章时一个比较常见的问题是:文章插图太宽,使整个网页都变形了。如果对每个插图都先进行缩放再插入的话,太麻烦了。

  9. jquery ajaxfileupload异步上传插件

    这篇文章主要为大家详细介绍了jquery ajaxfileupload异步上传插件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. React学习之受控组件与数据共享实例分析

    这篇文章主要介绍了React学习之受控组件与数据共享,结合实例形式分析了React受控组件与组件间数据共享相关原理与使用技巧,需要的朋友可以参考下

返回
顶部