【前端知识点总结】前端跨域问题

在前端开发中,"跨域"是一个绕不开的话题。无论是调用第三方 API,还是前后端分离项目中的本地联调,我们都可能遇到它。

一、什么是跨域问题?

要理解跨域,首先要知道什么是同源策略

同源策略(Same-Origin Policy)是浏览器最核心也是最基本的安全功能。它规定了一个源(origin)的文档或脚本,不能读取或修改另一个源的文档属性。

所谓同源 ,指的是协议、域名、端口号三者完全相同。

举个例子:

|----------------------------|--------------------------------------|----------|-------------------------------------------|
| URL A | URL B | 是否同源 | 原因 |
| http://www.example.com/ | http://www.example.com/dir/page.html | 是 | 协议、域名、端口均相同 |
| http://www.example.com/ | https://www.example.com/ | 否 | 协议不同 (http vs https) |
| http://www.example.com/ | http://api.example.com/ | 否 | 域名不同 (www.example.com vs api.example.com) |
| http://www.example.com:80/ | http://www.example.com:8080/ | 否 | 端口不同 (80 vs 8080) |

跨域(Cross-Origin) 就是指一个源的文档或脚本试图请求另一个源的资源。当浏览器发现这是一个跨域请求,并且该请求不符合某些安全例外(如 CORS),浏览器就会出于安全考虑,阻止该请求或限制对响应的访问,这就是我们常说的跨域问题。

注:跨域问题本质上是浏览器的行为。服务器之间(如后端服务 A 调用后端服务 B)的 HTTP 请求不存在跨域问题。

二、前端为什么会有跨域问题?

同源策略的存在主要是为了保护用户信息安全,防止恶意网站窃取数据。想象一下,如果没有同源策略:

  1. 你登录了网上银行 https://mybank.com,浏览器保存了你的登录凭证(Cookie)。
  2. 你在不经意间访问了一个恶意网站 https://evil.com
  3. 这个恶意网站的页面里有一段 JavaScript 代码,向 https://mybank.com/api/transfer?to=hacker\&amount=10000 发起了请求。
  4. 由于没有同源策略,浏览器会自动附上 mybank.com 的 Cookie,服务器会验证通过,执行转账操作。
  5. 你的钱就这样被悄无声息地转走了。

同源策略就是为了防止这种情况发生。它限制了 evil.com 的脚本读取 mybank.com 返回的响应数据,从而保护了用户信息。

三、如何解决跨域问题?

既然跨域是浏览器的一种安全限制,那么解决方案也必然围绕如何"告诉"浏览器这个跨域请求是安全的,或者如何绕过这个限制。以下是几种主流的解决方案:

1. CORS (Cross-Origin Resource Sharing) - 跨域资源共享

这是目前最推荐、最规范的解决方案。它是一种 HTTP 机制,允许服务器标示除了它自己以外的其他 origin(域、协议或端口),这样浏览器就可以访问加载这些资源。

CORS 的工作原理是:当浏览器发起一个跨域请求时,它会自动在请求头中添加一些信息(如 `Origin`),服务器根据这些信息判断是否允许该跨域请求,并在响应头中返回相应的许可信息。浏览器收到响应后,如果检查到服务器允许该请求,就不会报错。

简单请求预检请求

CORS 将请求分为两类:

  • 简单请求:满足一定条件(如方法是 GET/POST/HEAD,Content-Type 为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain)的请求。浏览器会直接发送请求,并在响应头中检查 Access-Control-Allow-Origin。
  • 非简单请求:如使用 PUT、DELETE 方法,或 Content-Type 为 application/json 的请求。浏览器会先发送一个 OPTIONS 方法的"预检请求"(Preflight Request)到服务器,询问是否允许该跨域请求。服务器确认允许后,浏览器才会发送真正的请求。

服务端配置示例 (Node.js + Express):

javascript 复制代码
const express = require('express');
const app = express();
const port = 3001; // 后端服务端口

// 允许所有源跨域 (仅用于开发,生产环境应指定具体域名)
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*'); // 或 'http://localhost:3000'
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With');
  // 如果需要携带 Cookie
  // res.header('Access-Control-Allow-Credentials', 'true');
  
  // 处理预检请求
  if (req.method === 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

app.get('/api/data', (req, res) => {
  res.json({ message: '这是来自跨域服务器的数据!' });
});

app.listen(port, () => {
  console.log(`后端服务运行在 http://localhost:${port}`);
});

前端调用示例 (使用 Fetch API):

javascript 复制代码
// 假设前端运行在 http://localhost:3000
fetch('http://localhost:3001/api/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error:', error));

企业开发实践:

在生产环境中,Access-Control-Allow-Origin 不应设置为 *,而应设置为前端应用的域名,如 https://www.myapp.com,以增强安全性。通常,这些 CORS 头会在反向代理(如 Nginx)或 API 网关层面统一配置。

2. 代理服务器

代理服务器是解决跨域问题的"万能钥匙"。其核心思想是:浏览器有跨域限制,但服务器之间没有。

原理:

  1. 前端应用向同源的代理服务器发送请求。
  2. 代理服务器接收到请求后,将其转发给真正的目标后端服务器(跨域)。
  3. 后端服务器将响应返回给代理服务器。
  4. 代理服务器再将响应返回给前端应用。

对于前端来说,它始终是在和同源的代理服务器通信,因此不存在跨域问题。

企业开发实践:

开发环境: 前端构建工具(如 Vite, Webpack)通常内置了代理功能。Vite ( vite.config.js ):

javascript 复制代码
import { defineConfig } from 'vite';
export default defineConfig({
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3001', // 真实后端服务地址
        changeOrigin: true, // 修改请求头中的 Origin 为目标地址
        // 可选:重写路径
        // rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  }
});

注意:前端代码中请求 /api/data,Vite 开发服务器会自动将其代理到 http://localhost:3001/api/data。

Webpack (vue.config.js 或 webpack.config.js):

javascript 复制代码
module.exports = {
 devServer: {
   proxy: {
     '/api': {
       target: 'http://localhost:3001',
       changeOrigin: true,
       pathRewrite: { '^/api': '' } // 重写路径,移除 /api
     }
   }
 }
};

生产环境: 通常使用 Nginx 作为反向代理。

Nginx 配置示例 (nginx.conf):

javascript 复制代码
server {
   listen 80;
   server_name www.myapp.com;
   location / {
       root /usr/share/nginx/html;
       index index.html;
       try_files $uri $uri/ /index.html; # SPA 路由支持
   }

   location /api/ {
       proxy_pass http://backend-server:3001/; # 后端服务地址
       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;
  }
}

这样,前端访问 www.myapp.com/api/data,Nginx 会将其代理到 http://backend-server:3001/data。

3. JSONP (JSON with Padding) - 仅支持 GET 请求

JSONP 是一种比较"古老"的跨域解决方案,它利用了 <script> 标签不受同源策略限制的特性。

原理:

  1. 前端定义一个回调函数,如 handleResponse 。
  2. 创建一个 <script> 标签,其 src 指向跨域 API,并将回调函数名作为参数传递,如 http://api.example.com/data?callback=handleResponse
  3. 服务器接收到请求后,将数据包裹在回调函数中返回,如 handleResponse({"name": "Alice", "age": 25}); 。
  4. 浏览器接收到并执行这段 JavaScript,从而调用前端定义的回调函数,并将数据作为参数传入。

服务端示例 (Node.js + Express):

javascript 复制代码
const express = require('express');
const app = express();
const port = 3002;

app.get('/api/jsonp', (req, res) => {
  const callbackName = req.query.callback;
  const data = { message: '这是 JSONP 返回的数据!' };
  const script = `${callbackName}(${JSON.stringify(data)})`;
  res.send(script);
});

app.listen(port, () => {
  console.log(`JSONP 服务运行在 http://localhost:${port}`);
});

前端调用示例:

javascript 复制代码
function handleResponse(data) {
  console.log('JSONP 响应:', data);
}

function loadJSONP() {
  const script = document.createElement('script');
  script.src = 'http://localhost:3002/api/jsonp?callback=handleResponse';
  document.body.appendChild(script);
  
  // 可选:请求完成后移除 script 标签
  script.onload = () => {
    document.body.removeChild(script);
  };
}

loadJSONP();

缺点:

  • 只支持 GET 请求。
  • 安全性较低,容易受到 XSS 攻击(如果服务器对回调函数名过滤不严)。
  • 错误处理困难。

现在,CORS 已经普及,JSONP 逐渐被淘汰,但在与一些只支持 JSONP 的老旧第三方服务交互时,可能还会用到。

4. 其他方案(了解即可)

  • WebSocket: WebSocket 协议不受同源策略限制,可以进行跨域通信。
  • postMessage: 用于不同窗口(iframe、popup)之间的安全跨域通信。
  • document.domain: 只适用于主域相同、子域不同的情况(如 a.example.comb.example.com ),现在已不推荐使用。

四、方案的对比与选择

  1. 跨域是浏览器安全策略(同源策略)导致的问题,目的是保护用户数据。
  2. CORS 是现代 Web 开发解决跨域问题的标准方案,需要后端服务器设置特定的 HTTP 响应头。
  3. 代理服务器是一种非常实用的绕过方案,尤其在开发环境和需要统一 API 网关的生产环境中。
  4. JSONP 是一种过时的技术,仅在特定兼容性需求下考虑。
相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅3 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊3 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax