跨域解决方案

一、跨域解决方案介绍

前端跨域问题是指在浏览器中,当一个 Web 应用程序尝试从一个不同域名、端口或协议发送请求时,浏览器会限制此类请求,以确保安全性。以下是几种常见的前端跨域解决方案:

  1. CORS(跨域资源共享): CORS 是一种由服务器端实现的解决方案,通过设置响应头来告诉浏览器哪些域名允许访问资源。服务器可以在响应头中添加 Access-Control-Allow-Origin 字段,指定允许的域名。

  2. JSONP(JSON with Padding): JSONP 是一种利用 <script> 标签进行跨域请求的方法。它通过在请求中包含一个回调函数名称,并返回一个以该函数名作为参数的脚本,从而绕过浏览器的同源策略限制。

  3. 代理服务器: 在同一个域名下设置一个代理服务器,然后由这个代理服务器来请求其他域名的资源。前端应用程序只需要与代理服务器通信,而代理服务器再与其他域名通信,避免了直接的跨域请求。

  4. CORS 代理: 一些服务提供了 CORS 代理,允许你通过这个代理来请求其他域名的资源。你将请求发送到这个代理,代理服务器会将请求发送到目标域名并将响应返回给你。

  5. WebSockets: 使用 WebSockets 可以建立持久的双向通信通道,这样就可以与不同域的服务器通信。WebSocket 协议不受同源策略限制。

  6. iframe 嵌套: 在同一个域名下,可以使用 <iframe> 标签嵌套其他域名的内容。虽然有一些限制,但在某些场景下可以解决一些跨域问题。

  7. LocalStorage 或 Cookie 共享: 如果需要在同一个浏览器中共享数据,可以使用 LocalStorage 或 Cookie 进行数据存储。这两者不受同源策略的限制。

需要根据具体场景选择合适的解决方案。CORS 是目前推荐的标准解决方案,因为它在服务器端进行配置,使得前端代码更简洁,同时也更符合安全性要求。

二、CORS(跨域资源共享)

CORS(跨域资源共享)

三、JSONP 解决方案

3.1 jsonp 实现

JSONP(JSON with Padding)是一种用于跨域请求的方法,它利用 <script> 标签来加载远程脚本,并将数据作为函数调用的参数返回。这样可以绕过浏览器的同源策略限制。以下是一个简单的 JSONP 示例:

假设你有一个服务器上的 JSONP 服务端点,它返回一个名为 callback 的参数作为函数名,和一些数据。你想要从另一个域名的前端应用中访问这个服务。以下是一个示例:

  1. JSONP 服务端点:

假设 JSONP 服务端点的 URL 是 http://example.com/jsonp,它返回的数据如下:

callbackFunction({ "message": "Hello, JSONP!" });
  1. 前端应用代码:

在前端应用中,你可以创建一个 <script> 标签来加载 JSONP 数据。需要在 URL 中包含一个参数,通常是 callback,其值是你希望的函数名。当服务器返回数据时,会调用这个函数,并将数据作为参数传递给它。

<!DOCTYPE html>
<html>
  <head>
    <title>JSONP Example</title>
  </head>
  <body>
    <script>
      // 创建一个全局函数作为 JSONP 的回调函数
      function callbackFunction(data) {
        console.log(data.message); // 输出:Hello, JSONP!
      }

      // 创建一个 <script> 标签来加载 JSONP 数据
      var script = document.createElement("script");
      script.src = "http://example.com/jsonp?callback=callbackFunction";
      document.body.appendChild(script);
    </script>
  </body>
</html>

在这个示例中,callbackFunction 是一个全局函数,用于接收从 JSONP 服务端点返回的数据。通过将函数名 callback 作为参数传递给服务端点 URL,服务器会返回一个类似 callbackFunction({ ... }) 的脚本,从而触发前端的回调函数。

请注意,JSONP 存在一些安全风险,因为它依赖于信任远程服务器提供的代码。因此,在使用 JSONP 时,应确保你信任提供 JSONP 服务的服务器。

3.2 jsonp 缺点

尽管 JSONP 是一种可以绕过浏览器同源策略的跨域请求方法,但它也有一些缺点和局限性:

  1. 安全性问题: JSONP 请求将执行远程服务器提供的脚本,因此存在安全风险。恶意服务器可能返回恶意代码,从而导致安全漏洞,如 XSS(跨站脚本攻击)。

  2. 仅支持 GET 请求: JSONP 只能通过 <script> 标签加载数据,因此只支持 GET 请求。这意味着你无法使用其他 HTTP 方法(如 POST、PUT)来发送请求。

  3. 没有错误处理机制: JSONP 请求没有内置的错误处理机制。如果请求失败或返回的脚本格式不正确,浏览器无法提供详细的错误信息。

  4. 限制数据格式: JSONP 服务器返回的数据必须是可执行的 JavaScript 代码,通常使用函数调用来传递数据。这限制了返回数据的格式,不如使用 JSON 那样灵活。

  5. 难以取消请求: JSONP 请求一旦发出,很难取消。当你不再需要请求数据时,很难中止请求,因为加载脚本后将自动执行。

  6. 无法设置请求头部: JSONP 请求的头部无法像普通的 XMLHttpRequest 或 Fetch 请求那样设置,这可能限制了某些高级需求,如自定义头部、设置认证等。

  7. 对跨域资源共享 (CORS) 的替代: JSONP 是在 CORS 之前使用的解决方案。虽然它可以绕过同源策略,但它没有 CORS 那样的丰富特性,如灵活的请求头、预检请求等。

考虑到这些缺点,现代 Web 开发通常倾向于使用更安全、灵活的跨域解决方案,如 CORS、代理服务器等。 JSONP 在特定情况下仍然有用,但使用时需要认识到它的局限性和潜在的安全风险。

四、代理服务器跨域

4.1 代理服务器实现

设置代理服务器来处理跨域请求是一种常见的方法,特别是在开发阶段。代理服务器作为中间层,将你的请求发送到目标服务器,然后将响应返回给你的应用程序。这可以绕过浏览器的同源策略限制。以下是设置代理服务器的一般步骤:

  1. 选择代理服务器: 你可以选择使用诸如 Node.js(Express)、Python、Nginx 等工具来创建代理服务器。选择一个你熟悉且适合你需求的工具。

  2. 设置代理服务器: 在代理服务器上创建一个路由,用于捕获你的应用程序发出的请求。

  3. 转发请求: 在代理服务器的路由中,将收到的请求转发到目标服务器。

  4. 获取响应: 等待目标服务器返回响应,然后将响应传递回你的应用程序。

下面以 Node.js(使用 Express 框架)为例,演示如何设置一个简单的代理服务器来处理跨域请求:

  1. 安装 Express: 首先,确保你已经安装了 Node.js 和 npm。然后通过以下命令安装 Express:

    npm install express
    
  2. 创建代理服务器: 创建一个名为 proxy.js 的文件,并在其中编写以下代码:

    const express = require("express");
    const httpProxy = require("http-proxy");
    
    const app = express();
    const proxy = httpProxy.createProxyServer();
    
    // 设置代理路由
    app.use("/api", (req, res) => {
      // 在这里将请求转发到目标服务器
      proxy.web(req, res, { target: "http://api.example.com" });
    });
    
    // 启动代理服务器
    const port = 3000;
    app.listen(port, () => {
      console.log(`Proxy server is listening on port ${port}`);
    });
    

    不使用 express,直接使用 http 库

    const http = require("http");
    const httpProxy = require("http-proxy");
    
    // 创建代理实例
    const proxy = httpProxy.createProxyServer({});
    
    // 创建代理服务器
    const proxyServer = http.createServer((req, res) => {
      // 设置目标服务器的地址
      const targetUrl = "http://api.example.com";
      // 执行代理转发
      proxy.web(req, res, { target: targetUrl });
    });
    
    // 监听代理服务器端口
    const port = 3000;
    proxyServer.listen(port, () => {
      console.log(`Proxy server is listening on port ${port}`);
    });
    

    这个例子创建了一个代理服务器,监听在本地的 3000 端口上。当你的应用程序发出 /api 开头的请求时,代理服务器会将请求转发到 http://api.example.com

  3. 启动代理服务器: 在终端中运行以下命令来启动代理服务器:

    node proxy.js
    
  4. 前端使用代理: 在你的前端应用程序中,将请求的 URL 改为代理服务器的 URL。例如,如果之前的请求是 http://api.example.com/data,现在改为 http://localhost:3000/api/data

通过设置代理服务器,你的前端应用程序可以绕过跨域问题,将请求发送给代理服务器,再由代理服务器将请求转发给目标服务器,并将响应返回给前端应用程序。请注意,生产环境中可能需要进一步的配置和安全措施来保护代理服务器。

4.2 REACT-CRA 实现代理服务器跨域

在 src 中增加文件 src/setupProxy.js

  proxySetup: resolveApp('src/setupProxy.js'),

src/setupProxy.js

const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1", {
      target: "https://1111",
      changeOrigin: true,
      ws: true,
      pathRewrite: { "^/api1": "" },
    })
  );

  app.use(
    createProxyMiddleware("/api2", {
      target: "https://2",
      changeOrigin: true,
      ws: true,
      pathRewrite: { "^/api2": "" },
    })
  );
};

五、CORS(跨域资源共享)代理

5.1 CORS 代理介绍

CORS(跨域资源共享)代理是一种在前端应用中解决跨域访问问题的方法之一。跨域访问是指在浏览器中,当一个网页的 JavaScript 代码尝试从一个不同域名的服务器请求资源时,会受到浏览器的同源策略限制,这是出于安全考虑的限制。

CORS 代理的基本原理是,在前端应用中,通过向同源的服务器发送请求,然后由该服务器代为请求目标资源,最后再将资源返回给前端。这样做的好处是,前端请求的实际目标是同源的,避免了跨域问题,而服务器端则负责处理实际的跨域请求。

以下是使用 CORS 代理的一般步骤:

  1. 前端请求: 在前端应用中,发送请求到同源服务器,将请求的信息作为参数传递给服务器。

  2. 同源服务器处理: 同源服务器收到请求后,根据请求的信息,发起真正的跨域请求到目标服务器,获取目标资源。

  3. 目标服务器响应: 目标服务器响应请求,将获取的资源返回给同源服务器。

  4. 同源服务器响应: 同源服务器将目标服务器的响应返回给前端应用,前端应用得到所需的资源。

这种方法的优势在于前端应用无需直接与目标服务器通信,避免了浏览器的跨域限制。同时,服务器端处理跨域请求,可以进行更精确的控制和验证。

需要注意的是,使用 CORS 代理需要确保同源服务器能够正常地与目标服务器通信,并且服务器端需要具备相应的逻辑来处理代理请求。另外,一些浏览器的安全策略可能会限制代理的使用,因此需要根据实际情况进行测试和调整。

综上所述,CORS 代理是一种可以解决跨域问题的方式,但在实际应用中需要谨慎考虑安全性和可行性。

5.2 CORS 代理实现

以下是一个简单的示例代码,演示如何在前端应用中使用 CORS 代理来获取跨域资源。在这个示例中,前端应用将请求发送给同源的服务器,然后由同源服务器代为请求目标资源,并将资源返回给前端应用。

前端应用代码:

async function fetchResource() {
  const proxyUrl = "https://your-proxy-server.com/proxy"; // CORS代理服务器地址
  const targetUrl = "https://target-server.com/resource"; // 目标资源地址

  try {
    const response = await fetch(proxyUrl, {
      method: "POST", // 可以使用GET、POST等请求方法
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ url: targetUrl }), // 将目标资源地址传递给代理服务器
    });

    const data = await response.json();
    console.log(data); // 输出代理服务器返回的资源数据
  } catch (error) {
    console.error("Error fetching resource:", error);
  }
}

fetchResource();

CORS 代理服务器代码(使用 Node.js 和 Express 示例):

const express = require("express");
const fetch = require("node-fetch");
const app = express();
const port = 3000;

app.use(express.json());

app.post("/proxy", async (req, res) => {
  const targetUrl = req.body.url;

  try {
    const response = await fetch(targetUrl);
    const data = await response.json();

    res.json(data); // 将目标服务器的响应返回给前端应用
  } catch (error) {
    res.status(500).json({ error: "Error fetching resource" });
  }
});

app.listen(port, () => {
  console.log(`CORS proxy server listening at http://localhost:${port}`);
});

在这个示例中,前端应用通过调用 fetchResource 函数来发送请求。该请求会被转发到代理服务器上,代理服务器会根据请求中的目标资源地址,发起实际的跨域请求,并将获取的资源返回给前端应用。

需要注意的是,代理服务器的实现会因使用的后端框架和语言而有所不同。这里使用了 Node.js 和 Express 来实现一个简单的代理服务器。

在实际应用中,你需要根据代理服务器的部署情况和需求进行适当的配置和调整。同时,注意确保代理服务器和前端应用之间的通信是安全可靠的。

六、iframe+postMessage 实现跨域

使用 iframe 嵌套和 postMessage 可以在不同的域之间进行安全的通信。这在处理跨域问题时特别有用。以下是一个基本的示例,演示了如何在父窗口和嵌套的 iframe 之间使用 postMessage 进行通信。

假设你有一个包含 iframe 的父页面和一个在 iframe 中的子页面。你希望它们能够相互通信,即使它们位于不同的域。

在父页面中:

// 获取嵌套的 iframe 元素
const iframe = document.getElementById("myIframe");

// 监听消息事件
window.addEventListener("message", (event) => {
  // 确保消息来自特定域
  if (event.origin === "http://子域名.com") {
    // 处理来自 iframe 的消息
    console.log("收到消息:", event.data);
  }
});

// 向 iframe 发送消息
iframe.contentWindow.postMessage("Hello from parent!", "http://子域名.com");

在 iframe 页面中:

// 监听消息事件
window.addEventListener("message", (event) => {
  // 确保消息来自特定域
  if (event.origin === "http://父域名.com") {
    // 处理来自父页面的消息
    console.log("收到消息:", event.data);

    // 向父页面发送回复消息
    parent.postMessage("Hello from iframe!", "http://父域名.com");
  }
});

在这个例子中,父页面和嵌套的 iframe 页面都通过监听 message 事件来接收消息,并通过 postMessage 方法来发送消息。使用 event.origin 来验证消息的来源域,以确保安全性。

请注意,实际应用中,你需要将 http://父域名.comhttp://子域名.com 替换为你实际使用的域名。

这种方法允许你在跨域的情况下实现页面之间的安全通信。但是,仍然需要注意安全性和跨站脚本攻击(XSS)等问题。

window.parent

window.parent 是 JavaScript 中一个指向包含当前窗口的父窗口的引用。它通常在嵌套的 iframe 中使用,可以用来访问嵌套页面的父页面。

如果你在一个嵌套的 iframe 页面中使用 window.parent,那么它会返回包含当前 iframe 的父页面的 window 对象。这可以用来从 iframe 页面中与父页面进行通信或访问父页面的属性和方法。

例如,假设你在一个 iframe 页面中要访问父页面的某个全局变量:

// 在 iframe 页面中
const parentGlobalVar = window.parent.myGlobalVar;
console.log(parentGlobalVar); // 输出父页面的全局变量值

或者在 iframe 页面中触发父页面的函数:

// 在 iframe 页面中
window.parent.myFunction();

需要注意的是,由于安全性原因,浏览器要求在不同的域之间进行跨窗口通信时,需要使用 postMessage 等安全机制来进行通信,以避免潜在的安全问题。

七、cookie实现跨域分享

实现跨域 Cookie 共享需要满足以下条件:

  1. 顶级域相同:要共享 Cookie,两个域名必须在同一个顶级域下,比如 a.example.comb.example.com

  2. 设置域属性:在设置 Cookie 时,需要将 domain 属性设置为顶级域名。例如,如果你在 a.example.com 设置 Cookie,可以将 domain 属性设置为 .example.com,这样 Cookie 就会在顶级域名下被共享。

下面是一个示例,演示如何在不同的子域下共享 Cookie:

  1. 在域 A 设置 Cookie

    document.cookie = "sharedCookie=hello; domain=.example.com; path=/;";
    
  2. 在域 B 读取 Cookie

    const cookies = document.cookie.split("; ");
    for (const cookie of cookies) {
      const [name, value] = cookie.split("=");
      if (name === "sharedCookie") {
        console.log(`Value of sharedCookie in Domain B: ${value}`);
      }
    }
    

需要注意的是,浏览器对 Cookie 跨域共享有限制。首先,为了安全性考虑,浏览器只允许在设置 Cookie 时使用明确的顶级域名(如 .example.com)。其次,如果在域 A 设置了 HttpOnly 属性的 Cookie,它不会在浏览器中的 JavaScript 中可见,因此在域 B 是无法读取到的。

此外,浏览器对 Cookie 数量和大小也有一定的限制,因此如果需要共享大量的数据,可能需要考虑其他跨域通信方式。

Contributors: masecho, --