jsonp解决前端跨域问题

465 阅读4分钟

各位大佬我们今天聊聊前端热门八股-跨域问题

那什么叫从一个域到另一个域呢?

在 Web 开发中,"域"(或"源")是指一个特定的协议、域名和端口的组合。浏览器的同源策略限制了从一个源加载的文档或脚本与另一个源的资源进行交互。理解域的概念对于理解跨域资源共享(CORS)非常重要。

域的组成

一个域由以下三个部分组成:

  • 协议:如 http 或 https。

  •  域名:如 example.com。

  • 端口:如 80(HTTP 的默认端口)或 443(HTTPS 的默认端口)。

比如掘金的域

image.png

同源策略

同源策略是浏览器的一种安全机制,限制了从一个源加载的文档或脚本与另一个源的资源进行交互。只有当协议、域名和端口都相同时,两个 URL 才被认为是同源的。

但是为什么浏览器要阻止这种访问资源的行为,我们来说几个常见的跨域安全问题

1. 防止跨站请求伪造(CSRF)

  • 跨站请求伪造是一种攻击方式,攻击者诱导用户的浏览器在用户不知情的情况下执行不当的操作。例如,用户登录到银行网站后,攻击者可能会诱导用户访问一个恶意网站,该网站会在用户不知情的情况下向银行网站发送请求,执行转账等操作。

2. 防止跨站脚本攻击(XSS)

  • 跨站脚本攻击允许攻击者在其他网站的上下文中执行恶意脚本。通过限制跨域请求,浏览器可以减少攻击者在用户访问的其他网站上执行恶意脚本的机会。

3. 保护用户隐私

  • 浏览器的同源策略保护用户的敏感信息不被恶意网站访问。例如,用户的会话信息、登录状态和其他敏感数据通常存储在 cookies 中,限制跨域请求可以防止这些信息被不可信的来源访问。

4. 防止数据泄露

  • 如果没有同源策略,恶意网站可以轻松地从其他网站获取数据,可能导致数据泄露。例如,攻击者可以从用户访问的其他网站窃取个人信息、交易记录等。

5. 确保数据完整性

  • 同源策略确保数据的完整性,防止恶意网站在用户不知情的情况下修改或操纵数据。

跨域请求

当一个网页尝试从不同的源请求资源时,就会发生跨域请求。例如:

我们用fetch模拟一下跨域访问

首先初始化一个后端项目,并且运行在3000端口

//http 服务启动
// commonjs模块规范node早期 引入http模块
const http=require('http');
const server=http.createServer((req,res)=>{
    //异步回调 
    //当请求来到服务器后,该函数会被执行 req请求对象被解析,res响应对象被创建 http结束

    //发送响应体
    res.end('hello world');
});
    console.log('服务已启动,端口号:3000');
});

我们使用nodemon热更新让后端跑在3000端口,使用live'server启动前端在5500端口我们前端用fecth请求3000端口会发现报错了,浏览器报了一个跨域访问出错,所以即使在同一个局域网,端口号不同的情况下,这算一个跨域请求

我们今天用jsonp来实现这跨域资源访问

 <ul id="list"></ul>
<script src=''http://localhost:3000''></script>
<script>
function callback(data) {
 list.innerHTML = data.map(user => `<li>${user.id+user.name}</li>`).join('');
}
</script>
                      

后端

//http 服务启动
// commonjs模块规范node早期 引入http模块
const http=require('http');
const user=[
    {
    id:1,
    name:'张三'
    },
    {
    id:2,
    name:'李四'
    }]
const server=http.createServer((req,res)=>{
    //异步回调 
    //当请求来到服务器后,该函数会被执行 req请求对象被解析,res响应对象被创建 http结束

    //发送响应体
    res.end('callback('+JSON.stringify(user)+')');
});

server.listen(3000,()=>{
    console.log('服务已启动,端口号:3000');
});

首先我们把script的源设置在与后端同一个端口,这里小编设置在3000端口,我们把后端返回的数据使用js的api挂载在ul标签上,这段代码的script标签从3000端口加载资源,看似浏览器能正常解析后端返回的js数据但是

各位大佬先别运行这段代码,我们先来想想这段代码的致命问题是什么,不错script标签阻塞

image.png

image.png 我们在浏览器启动前端会发现控制台报了一个callback没定义的问题,其实这段代码按顺序执行的时候,浏览器先解析了上面的script标签,从3000端口加载资源,但是此时的前端执行了返回的callback函数,<script src="http://localhost:3000"> 直接加载了这个脚本,而没有定义 callback 函数,导致浏览器无法正确执行返回的 JavaScript 代码。所以我们引出今天的跨域解决方案jsonp

我们把回调函数,与跨域的src封装在jsonp函数中这样只要改动函数中的src便能正确的处理后端返回的数据

前端

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

</head>
<body>
    <ul id="list"></ul>
    <script>
            

        let list = document.getElementById('list');
        // function callback(data) {
        //     list.innerHTML = data.map(user => `<li>${user.id+user.name}</li>`).join('');
        // }
        // fetch('http://127.0.0.1:3000').then(res => res.json())
        // fetch('http://localhost:3000')
        let jsonp=(url,callback)=>{
            let oScript=document.createElement('script')
            oScript.src=url          
            document.body.appendChild(oScript)
            window.callback=callback
        }
        jsonp('http://localhost:3000',(user)=>{ list.innerHTML = user.map(user => `<li>${user.id+user.name}</li>`).join('');})
    </script>
</body>
</html>

我们把后端callback函数挂载到window上

  • 动态创建 

  • 浏览器允许从不同源加载脚本文件,因此可以利用 

  • 回调函数:

  • 服务器返回的数据被包装在一个回调函数中。客户端在请求时指定回调函数的名称,服务器将数据作为参数传递给这个回调函数。

  • 数据传输:

  • 服务器返回的响应是一个 JavaScript 文件,其中包含对回调函数的调用,并将数据作为参数传递。

后端

//http 服务启动
// commonjs模块规范node早期 引入http模块
const http=require('http');
const user=[
    {
    id:1,
    name:'张三'
    },
    {
    id:2,
    name:'李四'
    }]
const server=http.createServer((req,res)=>{
    //异步回调 
    //当请求来到服务器后,该函数会被执行 req请求对象被解析,res响应对象被创建 http结束

    //发送响应体
    res.end('callback('+JSON.stringify(user)+')');
});

server.listen(3000,()=>{
    console.log('服务已启动,端口号:3000');
});

我们重新打开浏览器我们发现后端返回的数据正确的被浏览器解析到页面上,这样我们便使用jsonp实现了简单的跨域资源访问