Skip to content
📈0️⃣

怎么处理跨域问题

跨域是指一个域下的文档或脚本试图请求另一个域下的资源。在浏览器的同源策略下,这是一种安全机制,用于防止 Web 应用程序通过脚本等方式从其他域名读取数据。然而,在某些情况下,我们可能需要跨域访问资源,此时就需要使用一些技术手段来实现跨域访问。

跨域的解决方案有很多种,下面将介绍几种常用的方法:

1. JSONP

JSONP(JSON with Padding)是一种非官方的传输协议,允许在服务器端集成脚本来达到跨域的目的。它的基本原理是通过动态创建<script>标签,利用其 src 属性实现跨域请求。服务器端需要对返回的数据进行封装,将其放入一个回调函数中。

客户端代码示例:

html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>JSONP示例</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
        function handleResponse(data) {
            console.log(data);
        }
    </script>
</head>
<body>
    <script>
        $("body").append('<script src="http://example.com/data?callback=handleResponse"></script>');
    </script>
</body>
</html>

服务器端代码示例(Node.js):

javascript
const http = require("http");
const url = require("url");
const querystring = require("querystring");

http
  .createServer((req, res) => {
    const query = url.parse(req.url, true).query;
    const callback = query.callback;
    const data = { key: "value" };

    res.setHeader("Content-Type", "application/javascript");
    res.end(`${callback}(${JSON.stringify(data)})`);
  })
  .listen(3000);

2. CORS

CORS(跨域资源共享)是一种官方的跨域解决方案,它允许服务器在响应头中添加特定的字段,以告知浏览器允许跨域请求。浏览器会根据这些字段来判断是否允许跨域请求。

客户端代码示例:

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>CORS示例</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
      $.ajax({
        url: "http://example.com/data",
        type: "GET",
        dataType: "json",
        success: function (data) {
          console.log(data);
        },
        error: function (error) {
          console.log(error);
        },
      });
    </script>
  </head>
  <body></body>
</html>

服务器端代码示例(Node.js):

javascript
const http = require("http");
const cors = require("cors");

http.createServer(cors()).listen(3000);

如果你用的 koa 框架,可以使用 koa-cors 中间件来简化处理。参见本站 koa-cors 文档

3. 服务器代理

服务器代理是另一种跨域解决方案,它的原理是在服务器端创建一个代理接口,将客户端的请求转发到目标服务器。这样,客户端实际上是在请求自己的服务器,而不是直接请求目标服务器,从而避免了跨域问题。

客户端代码示例:

html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>服务器代理示例</title>
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script>
      $.ajax({
        url: "/proxy",
        type: "GET",
        dataType: "json",
        success: function (data) {
          console.log(data);
        },
        error: function (error) {
          console.log(error);
        },
      });
    </script>
  </head>
  <body></body>
</html>

服务器端代码示例(Node.js):

javascript
const http = require("http");
const request = require("request");

http
  .createServer((req, res) => {
    if (req.url === "/proxy") {
      request("http://example.com/data", (error, response, body) => {
        if (!error && response.statusCode == 200) {
          res.setHeader("Content-Type", "application/json");
          res.end(body);
        } else {
          res.statusCode = 500;
          res.end("Error occurred");
        }
      });
    } else {
      res.statusCode = 404;
      res.end("Not found");
    }
  })
  .listen(3000);

除了 JSONP、CORS 和服务器代理之外,还有其他几种技术可以解决跨域问题。下面将补充介绍几种方法:

4. 配置 Nginx 解决跨域

通过配置 Nginx 服务器,可以在服务器端添加必要的 HTTP 响应头来实现 CORS。在 Nginx 配置文件中,可以通过add_header指令添加Access-Control-Allow-Origin等 CORS 相关的头字段。

Nginx 配置示例:

nginx
server {
    listen       80;
    server_name  localhost;

    location / {
        # 允许来自所有域名的跨域请求
        add_header 'Access-Control-Allow-Origin' '*';
        # 设置允许的方法
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        # 设置允许的头部字段
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        # 设置预检请求的有效期
        add_header 'Access-Control-Max-Age' 1728000;
        # 支持客户端证书验证
        add_header 'Access-Control-Allow-Credentials' 'true';
        # 设置允许的暴露头部字段
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
    }

    # ...其他配置...
}

除了通过配置 Nginx 添加 CORS 相关的响应头来解决跨域问题外,还可以使用 Nginx 作为一个反向代理服务器,将客户端的请求转发到其他服务器,并将响应返回给客户端。这样,客户端实际上是在与 Nginx 服务器进行通信,而不是直接与目标服务器通信,从而避免了浏览器的同源策略限制。

以下是一个简单的 Nginx 配置示例,用于将客户端的请求转发到另一个服务器:

nginx
server {
    listen 80;
    server_name yourdomain.com;

    location / {
        # 设置反向代理
        proxy_pass http://target-server.com;
        # 设置客户端请求头的Host为原始Host
        proxy_set_header Host $host;
        # 设置客户端真实IP
        proxy_set_header X-Real-IP $remote_addr;
        # 设置客户端请求头中的Forwarded字段
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

在这个配置中,proxy_pass指令用于设置目标服务器的 URL。proxy_set_header指令用于设置转发请求时的 HTTP 头部信息。这样,当客户端发送请求到yourdomain.com时,Nginx 会将请求转发到http://target-server.com,并将响应返回给客户端。

需要注意的是,虽然这种方法可以解决跨域问题,但在某些情况下可能会带来安全风险。因此,在使用 Nginx 作为反向代理时,需要确保正确配置安全设置,例如限制可访问的 IP 地址、使用 HTTPS 等。

5. 通过 PostMessage 解决跨域

window.postMessage是一种安全机制,允许不同源之间的窗口进行通信。它不受到同源策略的限制。

发送消息的代码示例:

javascript
// 假设targetWindow是目标窗口的引用
targetWindow.postMessage("Hello", "http://example.com");

接收消息的代码示例:

javascript
window.addEventListener(
  "message",
  function (event) {
    // 检查来源是否安全
    if (event.origin !== "http://example.com") return;

    console.log("Received message:", event.data);
  },
  false
);

6. 通过 WebSocket 协议解决跨域

WebSocket 协议被设计为兼容现有的网络基础设施,包括防火墙和代理服务器。WebSocket 协议本身并不关心源的问题,因此在大多数情况下,WebSocket 连接不受同源策略的限制。

WebSocket 客户端代码示例:

javascript
const socket = new WebSocket("ws://example.com");

socket.onopen = function (event) {
  socket.send("Hello Server!");
};

socket.onmessage = function (event) {
  console.log("Message from server: ", event.data);
};

WebSocket 服务器端代码示例(Node.js,使用ws库):

javascript
const WebSocket = require("ws");

const wss = new WebSocket.Server({ port: 8080 });

wss.on("connection", (ws) => {
  ws.on("message", (message) => {
    console.log("Received: %s", message);
    ws.send("Hello Client!");
  });
});

总结:跨域问题的解决方法有很多种,可以根据具体的应用场景和需求选择合适的解决方案。例如,对于简单的 GET 请求,可以使用 JSONP;对于需要双向通信的场景,可以使用 CORS 或 PostMessage;对于实时通信,可以使用 WebSocket。在使用这些方法时,需要注意安全性和兼容性问题。