likes
comments
collection
share

"打破束缚,跨越边界"——浅析JavaScript中的跨域

作者站长头像
站长
· 阅读数 57

一、什么是跨域?

在Web前端开发中,跨域是指在浏览器中运行的JavaScript代码尝试访问不同域名、不同端口或不同协议的资源时所面临的安全限制。这种限制是浏览器出于安全考虑而引入的,以防止恶意脚本访问用户的敏感数据。

但是,这种出于安全的考虑也会给开发人员带来一定的麻烦。如图所示,在百度首页的页面下,打开控制台,分别输入以下两个命令去请求数据。

fetch("https://www.baidu.com")fetch("https://juejin.cn")

"打破束缚,跨越边界"——浅析JavaScript中的跨域

"打破束缚,跨越边界"——浅析JavaScript中的跨域

我们来看一下具体的报错信息。

Access to fetch at 'juejin.cn/' from origin 'www.baidu.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

翻译一下大概意思就是说: 来自'https://www.baidu.com'的请求'https://juejin.cn/'网站数据的行为已被CORS策略阻止:请求的资源上不存在Access Control Allow Origin头部信息。如果不透明响应满足您的需求,请将请求的模式设置为“无cors”,以在禁用cors的情况下获取资源。

二、为什么会出现跨域的问题?

上面那个报错是由于违反了浏览器的同源策略(Same-Origin Policy)而引起的,它是为了保护用户的安全而设立的一项安全措施。同源策略要求浏览器限制从一个源(origin)加载的网页或脚本与来自其他源的资源进行交互。

所谓"同源"指的是"三个相同"。

  • 1. 协议相同
  • 2. 域名相同
  • 3. 端口相同

同源的目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

例如,www.test.com/dir/index.h… 这个网址,协议是http ,域名是www.test.com, 端口是80(默认端口可以省略)。它的同源情况如下。

www.test.com/dir2/index2… test.com/dir/index.h… www.test2.com/dir/index.h… www.test.com/dir2/index.… www.test.com:81/dir/index.h…

三、JSONP

JSONP(JSON with Padding)是一种跨域解决方案,它利用script标签不受同源策略限制的特性来实现跨域数据获取。服务器将数据包装在一个JavaScript回调函数中,并以动态创建的script标签的形式返回给客户端。

三个允许加载跨域资源的标签。

1. <img src="XXX" >    //通常用于引入图片资源
2. <link href="XXX" >  // 通常用于引入css文件
3. <script src="XXX" > // 通常用于引入js文件

前端代码index2.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>跨域</title>
</head>
<body>
  <h1>我是前端的数据</h1>
  <script>
    function fn(data){
      console.log('@', data);
    }
  </script>
  <script src="http://localhost:3000?callback=fn"></script>
</body>
</html>

从?之后所写的东西表示客户端提供给服务器的额外参数,而这些参数是键值对形式的,用&进行分割。上面那种写法也可替换成下面这种写法。

<h1>我是前端的数据</h1>
  <script>
    let script = document.createElement('script');
    script.src = "http://localhost:3000?callback=fn"
    document.body.appendChild(script);
    function fn(data){
      console.log('@', data);
    }
  </script>

后端代码index2.js:

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

app.get('/', (req, res) => {
  var fn = req.query.callback;
  console.log(req.query);
  console.log(req.query.callback);
  // 返回跨域请求的响应数据 相当于 fn('我是后端服务器的数据!') 调用执行html里提前定义的函数
  res.send(fn + "('我是后端服务器的数据!')");
});
app.listen(3000, () => {
  console.log('JSONP项目已在3000端口上启动!');
});

"打破束缚,跨越边界"——浅析JavaScript中的跨域

"打破束缚,跨越边界"——浅析JavaScript中的跨域 JSONP这种方法相当于是在html代码里提前声明了一个回调函数,但是这个回调函数不会立即执行,等到后端传来数据时再调用触发。

JSONP兼容性好,但有一定的安全风险。JSONP只支持GET请求,并且需要前后端配合使用。

四、跨域资源共享(CORS)

CORS是一种现代化的跨域解决方案,它通过在服务器端设置响应头来允许跨域请求。服务器在响应中包含Access-Control-Allow-Origin头,指定允许访问资源的域名或通配符*。

CORS全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

index.html文件如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
  <title>跨域</title>
</head>
<body>
  <h1>我是前端的数据</h1>
  <button id="btn">请求数据</button>
  <script>
    let btn = document.getElementById('btn')
    btn.addEventListener("click", () => {
      $.ajax({
        url:'http://localhost:3000',
        method: 'get',
        headers: {
           // 为了告诉后端,你返回的响应头的数据类型应该是xxx
           'Content-Type': 'application/json; charset=utf-8'
        },
        success(res){
          console.log('请求成功了!');
          console.log(res);
        }
      })
    })
  </script>
</body>
</html>

index.js文件如下:

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

const app = express();
app.use(
  cors({
    origin: 'http://127.0.0.1:5500',
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
  }
)); // 使用cors中间件
app.get('/', (req, res) => {
  // 返回跨域请求的响应数据
  res.send('我是后端服务器的数据!');
});
app.listen(3000, () => {
  console.log('cors项目已在3000端口上启动!');
});

"打破束缚,跨越边界"——浅析JavaScript中的跨域

"打破束缚,跨越边界"——浅析JavaScript中的跨域

origin: 'http://127.0.0.1:5500' 这行代码设置了Access-Control-Allow-Origin响应头,表示你只允许特定域名进行访问。如果你希望允许所有来源(即任意域名)的请求访问资源,可以用*通配符表示允许任何域都能访问资源。

methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']

这行代码设置了Access-Control-Allow-Methods响应头,指定允许的HTTP请求方法。在示例中,我们设置了常见的HTTP方法,包括GET、POST、OPTIONS、DELETE和PUT。这表示允许客户端使用这些方法进行跨域请求。

具体的CORS设置可能因服务器框架而异。示例中使用的是Node.js的Express框架,通过调用res.header()方法来设置响应头部。其他服务器框架或语言也有相应的设置方式,但语法和方法可能有所不同。

前端代码相同,使用koa框架的写法如下。

const Koa = require('koa')
const cors = require('@koa/cors')

const app = new Koa()
app.use(
    cors({
        origin: 'http://127.0.0.1:5500'
    })
)
const main = (ctx) => {
    console.log(ctx.query);
    console.log(ctx.query.name);
    console.log(ctx.query.age);
    ctx.body = "这里是后端的数据!!!";
}
app.use(main)
app.listen(3000, () => {
    console.log('cors项目已经在3000端口上启动!');
})

"打破束缚,跨越边界"——浅析JavaScript中的跨域

"打破束缚,跨越边界"——浅析JavaScript中的跨域

五、代理服务器

浏览器有同源策略的限制,而服务器与服务器之间的访问是没有跨域问题的。

通过创建一个代理服务器,可以绕过浏览器的同源策略限制。客户端将请求发送到与自己同一域的服务器,然后由服务器转发请求到目标服务器,并将响应返回给客户端。这样,浏览器认为请求是发往同一域的,就绕过了浏览器的同源策略限制,从而实现跨域请求。有点像通过一个“中间人”来互相通信。

1.示例一 原生JS实现代理服务器

目标服务器

const Koa = require('koa')
const app = new Koa()

const main = (ctx,next) => {
    console.log(ctx.query.name);
    ctx.body = '我是后端服务器的数据!'
}
app.use(main)
app.listen(3000, () => {
    console.log('目标服务器已在3000端口启动');
})
const http = require('http') 
// 创建代理服务器
const server = http.createServer((req,res) => {
    // 开启 CORS,设置响应头
    res.writeHead(200, {
        // 允许跨域,配置同源白名单
        "Access-Control-Allow-Origin": "*",  
        // 允许的请求方法
        "Access-Control-Allow-Methods": "GET, POST, OPTIONS, PUT",
        // // 允许的请求头部
        "Access-Control-Allow-Headers": "Content-Type"
    }) 
    // 向其他后端发送请求获取数据
    http.request({
        host: '127.0.0.1', // 域名
        port: '3000', // 端口号
        path: '/',    // 路径
        method: 'GET'
    }, proxyRes => {
        // 接收代理服务器的响应数据
        proxyRes.on('data', result => {
            // 将响应数据返回给客户端
            res.end(result.toString())
        })
    }).end()
})
// 代理服务器监听在3001端口
server.listen(3001, () => {
    console.log('代理服务器已在3001端口启动');
})

2.示例二 在Vue项目中实现跨域

在Vue项目中实现跨域可以通过配置代理服务器来实现。以下是使用Vue CLI创建的Vue项目中如何跨域的步骤:

首先,安装 http-proxy-middleware 依赖:

npm install http-proxy-middleware --save

在项目根目录下创建一个 vue.config.js 文件,如果已经存在该文件则直接编辑它。

在 vue.config.js 中添加以下代码:

const proxyMiddleware = require('http-proxy-middleware');
module.exports = {
  devServer: {
    before: function(app) {
      app.use(
        '/api',
        proxyMiddleware({
          target: 'http://example.com', // 设置你的目标服务器地址
          changeOrigin: true,
          pathRewrite: {
            '^/api': '' // 如果你的接口路径有特殊前缀,可以在这里进行替换
          }
        })
      );
    }
  }
};

以上代码通过http-proxy-middleware中间件在开发服务器启动之前,配置了一个代理规则。该代理规则指定了需要转发的请求路径(/api)以及转发的目标服务器地址http://example.com

在你的Vue组件中,可以直接使用相对路径 /api 发起请求。例如:

// 在你的Vue组件中
axios.get('/api/data')
  .then(response => {
    console.log(response.data);
  })
  .catch(error => {
    console.error(error);
  });

通过上述配置,所有以 /api 开头的请求都会被转发到目标服务器地址,并获取返回的数据。

需要注意的是,在开发环境下,这种配置只会影响到开发服务器,而在生产环境下,你需要在后端服务器上进行相关跨域配置。

这样,通过配置代理服务器,就能在Vue项目中实现跨域请求了。代理服务器会帮助你将前端的请求转发到目标服务器,并将响应返回给前端,避免了浏览器的跨域限制。

六、最后的话

跨域大致共有九种左右解决方法,本文只介绍了三种,其他跨域方法,例如基于post message实现跨域处理基于iframe的跨域处理web-socket和nginx反向代理,以后有机会再详述。

跨域是Web开发中常见的问题,但通过使用JSONP、CORS或代理服务器等方法,我们可以有效地解决跨域限制,并实现安全的跨域数据交互。根据具体的场景和需求,选择适合的跨域解决方案能够提升开发效率和用户体验。

能力一般,水平有限,本文可能存在纰漏或错误,如有问题欢迎指正(评论或私信),感谢你阅读这篇文章,如果你觉得写得还行的话,不要忘记点赞、评论、收藏哦!祝生活愉快!

转载自:https://juejin.cn/post/7235603140263313465
评论
请登录