likes
comments
collection
share

常用的解决跨域问题的几种方法总结

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

跨域是指在浏览器端,由于浏览器的同源策略(Same Origin Policy)限制,导致无法直接访问来自其他域的资源。为了解决跨域问题,常用的方法包括使用 CORS(跨域资源共享)、JSONP、代理、WebSocket、跨文档消息传递(PostMessage)等。下面是对这些方法的简要说明以及代码示例:

1. CORS(跨域资源共享):

CORS 是一种标准的跨域解决方案,通过服务器设置响应头来允许跨域请求。

服务端示例(Node.js)

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

// 设置允许跨域访问的域名
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', '*'); // 或指定特定域名
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

app.get('/api/data', (req, res) => {
  res.json({ message: 'Hello from server!' });
});

app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

客户端示例(JavaScript)

fetch('http://localhost:3000/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

2. JSONP:

JSONP 是一种利用 <script> 标签不受同源策略限制的特性来实现跨域请求的方法。

客户端示例(JavaScript)

function handleResponse(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'http://example.com/api/data?callback=handleResponse';
document.body.appendChild(script);

在上面的例子中,客户端向 example.com/api/data 发送请求,并指定了 callback 参数为 handleResponse 函数名。服务器返回的数据将会作为参数传入 handleResponse 函数中,并执行该函数。

3. 代理:

在项目中设置一个代理服务器,让代理服务器去请求目标服务器的数据,然后再将数据返回给前端。由于同源策略是浏览器的限制,代理服务器是在服务器端操作,因此可以绕过同源策略的限制。

例如,使用 Express 框架实现一个简单的代理服务器:

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

const app = express();

// 设置代理路由
app.get('/api/data', async (req, res) => {
  try {
    const response = await axios.get('http://example.com/api/data');
    res.json(response.data);
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Internal Server Error' });
  }
});

// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Proxy server is running on http://localhost:${PORT}`);
});

4. WebSocket:

WebSocket 是一种基于 TCP 的全双工通信协议,它允许客户端和服务器之间进行双向通信,与传统的 HTTP 请求-响应模式不同,WebSocket 建立一次连接后,可以保持长时间的通信会话。

虽然 WebSocket 协议本身并不受同源策略的限制,但在使用 WebSocket 进行跨域通信时,仍然需要注意一些安全性问题。一种常见的做法是通过服务端设置 CORS 头部,允许特定的源访问 WebSocket 服务。

以下是一个使用 Node.js 和 ws 模块创建 WebSocket 服务器,并设置 CORS 头部的示例代码:

const WebSocket = require('ws');
const http = require('http');

// 创建 HTTP 服务器
const server = http.createServer((req, res) => {
  // 设置 CORS 头部
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('WebSocket server is running');
});

// 启动 HTTP 服务器
server.listen(3000, () => {
  console.log('HTTP server is running on http://localhost:3000');
});

// 创建 WebSocket 服务器
const wss = new WebSocket.Server({ server });

// 监听 WebSocket 连接
wss.on('connection', (ws) => {
  console.log('New WebSocket connection');

  // 接收客户端消息
  ws.on('message', (message) => {
    console.log(`Received message: ${message}`);

    // 发送消息给客户端
    ws.send(`Server received message: ${message}`);
  });

  // 监听 WebSocket 关闭
  ws.on('close', () => {
    console.log('WebSocket connection closed');
  });
});

在上面的示例中,我们首先创建了一个 HTTP 服务器,并设置了 CORS 头部,允许任何来源的请求访问该服务器。然后创建了一个 WebSocket 服务器,监听端口为 3000,当有 WebSocket 连接建立时,会输出一条日志信息。在收到客户端的消息后,服务器会将消息原样返回给客户端。

5. 跨文档消息传递(PostMessage):

跨文档消息传递(PostMessage)是一种在不同窗口或 iframe 之间安全地传递数据的机制,它允许在同源策略限制下的不同窗口之间进行通信。以下是一个简单的示例,演示了如何使用 PostMessage 在父窗口和子窗口之间进行通信:

父窗口 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Parent Window</title>
</head>
<body>
  <h1>Parent Window</h1>
  <iframe id="childFrame" src="child.html" width="300" height="200"></iframe>
  <script>
    // 获取子窗口的引用
    const childFrame = document.getElementById('childFrame').contentWindow;

    // 发送消息给子窗口
    childFrame.postMessage('Hello from parent window!', 'http://localhost:3000');
    
    // 监听来自子窗口的消息
    window.addEventListener('message', (event) => {
      if (event.origin === 'http://localhost:3000') {
        console.log('Received message from child window:', event.data);
      }
    });
  </script>
</body>
</html>

子窗口 HTML(child.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Child Window</title>
</head>
<body>
  <h1>Child Window</h1>
  <script>
    // 监听来自父窗口的消息
    window.addEventListener('message', (event) => {
      if (event.origin === 'http://localhost:3000') {
        console.log('Received message from parent window:', event.data);

        // 发送消息给父窗口
        event.source.postMessage('Hello from child window!', event.origin);
      }
    });
  </script>
</body>
</html>

在上面的示例中,父窗口和子窗口之间通过 PostMessage 进行了通信。父窗口首先获取了子窗口的引用,然后向子窗口发送了一条消息。子窗口监听来自父窗口的消息,收到消息后再向父窗口发送一条消息。两个窗口通过事件监听器监听对方的消息,并在收到消息后进行处理。

需要注意的是,PostMessage 方法的第二个参数 origin 是目标窗口的源,用于验证消息来源的安全性。在实际应用中,应该根据实际需求设置合适的 origin 值,确保消息的安全传递。

6.nginx反向代理

Nginx 来实现反向代理,通过反向代理可以解决跨域访问的问题。下面是一个简单的示例,演示了如何使用 Nginx 反向代理来转发请求:

假设有两个服务,一个运行在 http://localhost:3000,另一个运行在 http://localhost:4000,我们希望通过 Nginx 反向代理将请求转发到这两个服务上。

首先,需要安装 Nginx 并编辑配置文件。

安装 Nginx:

# Ubuntu 或 Debian 系统
sudo apt update
sudo apt install nginx

# CentOS 或 RedHat 系统
sudo yum install nginx

编辑 Nginx 配置文件:

sudo nano /etc/nginx/nginx.conf

在配置文件中添加以下内容:

http {
    server {
        listen       80;
        server_name  localhost;

        # 反向代理转发到第一个服务
        location /service1/ {
            proxy_pass http://localhost:3000/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }

        # 反向代理转发到第二个服务
        location /service2/ {
            proxy_pass http://localhost:4000/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

上面的配置中,我们在 Nginx 的配置文件中设置了两个 location 块,分别用于转发到第一个服务和第二个服务。通过 proxy_pass 指令可以指定目标服务的地址。proxy_set_header 指令用于设置代理请求头,确保目标服务能够正确获取到客户端的 IP 地址等信息。

保存并关闭文件后,重新加载 Nginx 配置:

sudo nginx -s reload

现在,Nginx 将会在端口 80 上监听请求,并根据请求的路径将其转发到对应的服务上。例如,访问 http://localhost/service1/ 将会转发到 http://localhost:3000/,访问 http://localhost/service2/ 将会转发到 http://localhost:4000/

通过这种方式,我们可以实现跨域访问,并将请求代理到不同的服务上。

7. 使用 window.name + iframe:

父窗口 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Parent Window</title>
</head>
<body>
  <h1>Parent Window</h1>
  <iframe id="childFrame" src="child.html" style="display:none;"></iframe>

  <script>
    window.onload = function() {
      var childFrame = document.getElementById('childFrame');
      childFrame.onload = function() {
        // 向子窗口传递消息
        childFrame.contentWindow.name = 'Hello from parent window!';
      };
    };
  </script>
</body>
</html>

子窗口 HTML(child.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Child Window</title>
</head>
<body>
  <h1>Child Window</h1>
  <script>
    // 从父窗口读取消息
    var message = window.name;
    console.log('Message from parent window:', message);
  </script>
</body>
</html>

8. 使用 location.hash + iframe:

父窗口 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Parent Window</title>
</head>
<body>
  <h1>Parent Window</h1>
  <iframe id="childFrame" src="child.html" style="display:none;"></iframe>

  <script>
    window.onload = function() {
      var childFrame = document.getElementById('childFrame');
      childFrame.onload = function() {
        // 向子窗口传递消息
        childFrame.contentWindow.location.hash = 'Hello from parent window!';
      };
    };
  </script>
</body>
</html>

子窗口 HTML(child.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Child Window</title>
</head>
<body>
  <h1>Child Window</h1>
  <script>
    // 从父窗口读取消息
    window.onload = function() {
      var message = window.location.hash;
      console.log('Message from parent window:', message);
    };
  </script>
</body>
</html>

9. 使用 document.domain + iframe:

父窗口 HTML:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Parent Window</title>
</head>
<body>
  <h1>Parent Window</h1>
  <iframe id="childFrame" src="child.html" style="display:none;"></iframe>

  <script>
    window.onload = function() {
      // 设置 document.domain
      document.domain = 'example.com';

      var childFrame = document.getElementById('childFrame');
      childFrame.onload = function() {
        // 向子窗口传递消息
        childFrame.contentWindow.postMessage('Hello from parent window!', 'http://child.example.com');
      };
    };
  </script>
</body>
</html>

子窗口 HTML(child.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Child Window</title>
</head>
<body>
  <h1>Child Window</h1>
  <script>
    // 监听来自父窗口的消息
    window.onload = function() {
      window.addEventListener('message', function(event) {
        console.log('Message from parent window:', event.data);
      });
    };
  </script>
</body>
</html>

在上面的7,8,9示例中,父窗口通过 window.namelocation.hashdocument.domain 向子窗口传递消息,子窗口则通过相应的方式接收消息,并在控制台输出。这些方法都可以用于实现简单的跨域通信。

10 Nodehttp-proxy-middleware 中间件代理

http-proxy-middleware 中间件可以用于在 Node.js 服务端设置代理,实现跨域请求的转发。即使涉及到两次跨域(客户端到代理服务器,代理服务器到目标服务器),http-proxy-middleware 也能够处理这种情况。

以下是一个简单的示例,演示了如何在 Node.js 中使用 http-proxy-middleware 中间件进行代理:

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// 设置代理
const proxy = createProxyMiddleware({
  target: 'http://example.com', // 目标服务器地址
  changeOrigin: true, // 修改请求头中的 Host 字段为目标服务器地址
  pathRewrite: {
    '^/api': '/', // 重写请求路径,将 /api 开头的路径替换为空
  },
});

// 使用代理中间件
app.use('/api', proxy);

// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Proxy server is running on http://localhost:${PORT}`);
});

在上述示例中,我们创建了一个代理中间件,将所有路径以 /api 开头的请求转发到目标服务器 http://example.com 上。通过 pathRewrite 选项,我们将请求路径中的 /api 前缀去除,以符合目标服务器的路径要求。

然后,我们将代理中间件应用到 Express 应用中,并启动了一个 Express 服务器,监听在本地的端口 3000 上。

客户端可以向这个 Express 服务器发送请求,请求路径以 /api 开头的请求将会被转发到目标服务器上,实现了代理转发功能。

最后也是全文最总要的,码字不易,欢迎点赞关注加搜藏,你的鼓励是我持之以恒的动力,感谢感谢感谢!!!

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