当前位置: 首页 > news >正文

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

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

一、什么是跨域问题?

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

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

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

举个例子:

URL AURL 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):

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):

// 假设前端运行在 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 ):

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):

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

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

Nginx 配置示例 (nginx.conf):

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):

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}`); });

前端调用示例:

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.com 和 b.example.com ),现在已不推荐使用。

四、方案的对比与选择

  1. 跨域是浏览器安全策略(同源策略)导致的问题,目的是保护用户数据。
  2. CORS 是现代 Web 开发解决跨域问题的标准方案,需要后端服务器设置特定的 HTTP 响应头。
  3. 代理服务器是一种非常实用的绕过方案,尤其在开发环境和需要统一 API 网关的生产环境中。
  4. JSONP 是一种过时的技术,仅在特定兼容性需求下考虑。
http://www.cnnetsun.cn/news/95997.html

相关文章:

  • 只需5个步骤带你了解渗透测试全过程,SSH端口22如何完全沦陷!
  • 一个漏洞2w+,网安副业挖SRC漏洞,躺着把钱挣了!挖漏洞平均一天收入多少?
  • 数据血缘追踪与质量监控实现方法
  • 【编程干货】大模型开发文档处理秘籍,让你的RAG系统性能提升10倍!
  • 【AI开发必备】Mini Agent:零门槛构建智能Agent,支持MCP工具和无限长任务,GitHub已爆![特殊字符]
  • 栈与队列学习笔记
  • Oracle回滚与撤销技术
  • 我的mybatis-flex自定义查询为什么没有参数
  • 揭秘Dify混合检索缓存机制:为何缓存清理如此重要?
  • 计划赶不上变化?错!是计划“根本赶不上开工”
  • 应用冷启动优化
  • java_base_(接口篇)省流版
  • 实测主流科技查新网站:它们如何解决专利与项目查新的双重需求?
  • 【收藏必备】零基础入门AI Agent:概念、结构、方法与开发框架全解析
  • vue基于Springboot框架实现新能源汽车4s店销售管理系统
  • 开关频率可调的永磁同步电机svpwm发电仿真模型,可调稳定发电电压,负载,母线电容可调,可用于...
  • C语言高阶玩法:函数指针与回调函数实战指南,让你的代码拥有“灵魂”
  • 基于SpringBoot的校园二手书交易平台的设计与实现
  • 数据结构与算法--007三数之和(medium)
  • C++ 模板初阶:泛型编程的入门指南
  • 基于Java实现优雅关闭的规范化方案设计与实现
  • 时序数据战场巅峰对决:金仓数据库 VS InfluxDB深度解析
  • Windows任务管理器中CPU相关指标怎么看?
  • 【必藏】大模型入行晚了?现在就是黄金时机!小白到入门的完整路线
  • 系统思考与认知习惯
  • 速藏!2026年免费免版权音乐素材网站推荐!正规版权保障,商用无压力不侵权
  • 【数据分享】1951-2024年我国省市县三级逐日、逐月和逐年近地面气温数据(Shp/Excel格式)
  • 金融行业广告投放:在合规的赛道上,实现精准增长
  • 长安汽车11月销量28.3万辆,同比增长2.3%
  • 1688 商品详情接口深度解析:从百川签名突破到供应链数据重构