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

为什么禁止我请求别的网站的接口?——跨域与CORS _

你有没有遇到过这种情况:在自己的网页上想请求别人的API,结果浏览器直接报错:Access-Control-Allow-Origin' header is missing。为什么浏览器要阻止你?服务器不响应不就完了吗?

今天,用小区门禁的故事,来讲讲跨域CORS

什么是"跨域"?

同源策略 — 浏览器的安全基石

浏览器有个同源策略Same-Origin Policy):只有来自同一个"家"的资源才能随便用。

什么叫"同一个家"?看三个条件:协议(http/https)、域名(example.com)、端口(:8080)。三个都一样,才是同源;有一个不一样,就是跨域。

跨域的例子

1

2

3

4

5

✅ http://example.com 和 http://example.com/profile // 协议+域名+端口都相同 → 同源

✅ https://example.com 和 https://example.com // 协议+域名+端口都相同 → 同源

❌ http://example.com 和 https://example.com // 协议不同 → 跨域

❌ http://example.com 和 http://api.example.com // 域名不同(子域名)→ 跨域

❌ http://example.com:8080 和 http://example.com:3000 // 端口不同 → 跨域

跨域限制了什么?

浏览器的同源策略主要限制了三件事:

  • DOM 访问:无法读取不同源的 iframe 内容、无法修改不同源的 iframe DOM
  • AJAX 请求:无法请求不同源的 API
  • Cookie/LocalStorage:无法访问不同源的数据

为什么要限制跨域?

模拟一个攻击场景

想象一下:你登录了银行网站,浏览器保存了你的登录 Cookie。

然后你手滑点进了一个恶意网站,这个网站里有一段代码:

1

2

3

4

5

<form href="https://urlscan.io/result/019dd18e-1f44-74de-a5be-32c1f5ea040c/"method="POST">

<input type="hidden"name="to"value="hacker">

<input type="hidden"name="amount"value="1000000">

</form>

<script>document.forms[0].submit();</script>

如果没有同源策略,这个表单请求会自动带上bank.com的 Cookie,银行服务器以为是你本人操作的——钱就没了。

同源策略就是浏览器的"门禁":只有同一家人才能进,陌生人要查证件。

💡 注意:<img>标签的 GET 请求虽然也会带 Cookie,但现代浏览器有SameSiteCookie 保护。上面表单 POST 场景更典型。


CORS — 跨域的"通行证"

CORS 是什么?

CORS(Cross-Origin Resource Sharing)= 跨域资源共享。

它的工作原理很简单:让服务器告诉浏览器,"我允许来自这些源的请求"

简单请求 vs 预检请求

简单请求

满足以下条件的请求是"简单请求":

简单请求的流程:

1

2

3

4

5

6

7

1. 浏览器发送请求(自动带上 Origin 头)

2. 服务器检查 Origin,决定是否允许

3. 服务器返回响应头 Access-Control-Allow-Origin

4. 浏览器检查响应头,允许就完事

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

1

2

3

4

5

6

7

8

9

app.get('/api/data', (req, res) => {

constorigin = req.headers.origin;

if(origin === '') {

res.setHeader('Access-Control-Allow-Origin', origin);

}

res.json({ data:'这是返回的数据'});

});

响应头:

1

2

3

4

5

HTTP/1.1 200 OK

Access-Control-Allow-Origin: https://example.com

<a href="https://urlscan.io/result/019dd18e-29e6-702b-b9f9-d2445b252501/"></a>

Content-Type: application/json

{"data":"这是返回的数据"}

预检请求(Preflight)

不满足"简单请求"条件的,浏览器会先发一个 OPTIONS 请求"探路":

1

2

3

4

5

6

7

1. 浏览器发送 OPTIONS 预检请求

2. 服务器检查方法/头部/Origin

3. 服务器返回允许的头 Access-Control-*

4. 浏览器发送实际请求

预检请求检查什么?

预检请求(OPTIONS)就像登机前的安检——先检查你带没带危险品。

浏览器会问服务器三件事:

  • 我从哪来?(Origin)
  • 我想用什么方法?(Access-Control-Request-Method)
  • 我想带什么头?(Access-Control-Request-Headers)

服务器回答"可以",浏览器才放行实际请求。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

# 请求(浏览器发给服务器)

OPTIONS /api/data HTTP/1.1

Origin: <a href="https://urlscan.io/result/019dd18e-35a3-76ab-99bf-0752d3378713/"></a> # 我从哪来

Access-Control-Request-Method: PUT # 我想用 PUT 方法

Access-Control-Request-Headers: Content-Type, Authorization # 我想带这些头

---

# 响应(服务器告诉浏览器)

HTTP/1.1 204 No Content

Access-Control-Allow-Origin: <a href="https://urlscan.io/result/019dd18e-4068-70d2-a85c-2bfa345b23aa/"></a> # 允许这个源

Access-Control-Allow-Methods: GET, POST, PUT, DELETE # 允许这些方法

Access-Control-Allow-Headers: Content-Type, Authorization # 允许这些头

Access-Control-Max-Age: 86400 # 预检结果缓存24小时

服务器端处理:

1

2

3

4

5

6

7

8

9

10

11

12

app.options('/api/data', (req, res) => {

constorigin = req.headers.origin;

if(origin === '<a href="https://urlscan.io/result/019dd18e-4c5a-71cc-b20b-a4e8ac2ec164/"></a>') {

res.setHeader('Access-Control-Allow-Origin', origin);

res.setHeader('Access-Control-Allow-Methods','GET, POST, PUT, DELETE');

res.setHeader('Access-Control-Allow-Headers','Content-Type, Authorization');

res.setHeader('Access-Control-Max-Age','86400');

}

res.status(204).send();

});

CORS 响应头详解

常用响应头

credentials 模式

默认情况下,CORS不带Cookie。如果需要携带 Cookie:

前端:

1

2

3

fetch('<a href="https://urlscan.io/result/019dd18e-58c4-72c9-b486-e6c5ab731092/"></a>', {

credentials:'include'

});

服务端:

1

2

res.setHeader('Access-Control-Allow-Origin', '<a href="https://urlscan.io/result/019dd18e-64fb-73dd-b51a-0601024a892f/"></a>');

res.setHeader('Access-Control-Allow-Credentials','true');

注意:Access-Control-Allow-Origin不能用*,必须是具体域名。


跨域的解决方案

1. JSONP(已不推荐)

利用<script>标签不受同源策略限制的特性:

1

2

3

4

5

6

<script>

function handleData(data) {

console.log(data);

}

</script>

<a href="https://urlscan.io/result/019dd18e-700f-705d-a721-278d4d15ac80/"></a>

2. 代理服务器

在自己的服务器上转发请求,"伪装"成同源:

1

浏览器 ──> 我的服务器(同一源) ──> 目标服务器

Nginx 代理:

1

2

3

location /api/ {

<a href="https://urlscan.io/result/019dd18f-3875-71a6-a17d-cffb86fc9a00/"></a>;

}

Node.js 代理:

1

2

3

4

5

app.get('/api/data', async (req, res) => {

constresponse = await fetch('<a href="https://urlscan.io/result/019dd18f-454d-7307-bbb2-ab0a62f529b5/"></a>');

constdata = await response.json();

res.json(data);

});

3. Webpack/Vite 开发代理

开发环境配置代理:

1

2

3

4

5

6

7

8

9

10

11

// vite.config.js

exportdefault{

server: {

proxy: {

'/api': {

target:'http://target-server.com',

changeOrigin:true

}

}

}

};

4. postMessage

不同窗口/iframe 之间的通信:

1

2

3

4

5

6

7

window.addEventListener('message', (event) => {

if(event.origin ==='https://example.com') {

console.log('收到消息:',event.data);

}

});

iframe.contentWindow.postMessage('hello','https://example.com');

深入了解 CORS 🔬

第三方 Cookie 的限制

现代浏览器正在逐步限制第三方 Cookie:

CORS 和 CSRF 的区别

为什么 OPTIONS 叫"预检"?

"预检"就像登机前的安检——先检查你带没带危险品(方法、头部),没问题了才让你登机(发送实际请求)。


常见错误排查

错误 1:No 'Access-Control-Allow-Origin' header

错误 2:Method not allowed

错误 3:Header not allowed

错误 4:预检请求 404

总结

写在最后

现在你应该明白了:

  • 跨域是浏览器的安全机制,不是为了刁难你
  • CORS 是服务器授权机制,服务器说可以,浏览器才放行
  • 预检请求= 安检,OPTIONS 通过了才能发送实际请求
  • 生产环境推荐用代理,开发环境用 webpack/vite 代理

下次遇到跨域错误,先看浏览器控制台的报错信息——是"缺通行证"(header 缺失)还是"通行证不对"(origin 不匹配),处理方式不一样的。

http://www.jsqmd.com/news/717961/

相关文章:

  • 艾体宝干货|【Redis实用技巧#17】语义缓存(Semantic Caching):LLM 的第一道防线
  • 颠覆传统:用Mac Mouse Fix重新定义macOS鼠标体验的完整指南
  • PyCharm装不上numpy?别急着重装,试试这5个国内镜像源(附最新可用地址)
  • 别再手动disconnect了!用Qt的QSignalBlocker优雅管理控件信号(附QComboBox实例)
  • MusePublic Art Studio部署教程:国产昇腾910B芯片适配SDXL的可行性验证
  • 第3章 三类客户端:Python Client、JavaScript Client与Curl Client(1)——使用Gradio Python Client
  • DeepSeek-V4 新手快速上手指南
  • cursor的使用指令
  • 别再傻傻重装Office了!一招搞定0xC004F074激活报错(附Software Protection服务自启动设置)
  • OpenProject完整指南:免费开源项目管理软件快速上手终极教程
  • 录屏长时间录制不卡顿不黑屏:通用解决方法+5款软件实操指南
  • Windows安装Redis和Fastapi联合使用
  • 3步掌握AMD Ryzen性能调校:SMUDebugTool终极指南
  • 2026中小企业AI超级员工选型:5款工具实测指南
  • GetQzonehistory:一键备份你的QQ空间所有历史说说,让青春记忆永不丢失
  • 零基础玩转Gemma-3-12B-IT:图形化界面快速部署与对话体验
  • Qianfan-OCR惊艳案例:手写会议记录→结构化待办事项+责任人分配
  • 2026年3月成套的化工装备供应商推荐,填料塔/煤化工设备/反应釜/化工装备/换热器/储罐,化工装备厂商哪家权威 - 品牌推荐师
  • 2026年3月技术好的小龙虾筛选机制造商推荐,小龙虾筛选设备/小龙虾筛选机/小龙虾分选机,小龙虾筛选机公司推荐 - 品牌推荐师
  • AI 聊天 API 集成指南
  • 快速上手:在星图AI上训练PETRV2-BEV模型,实现3D目标检测
  • # D3.js实战进阶:从基础图表到交互式数据仪表盘的全流程构建在现代前端开发中,**数据可视化已成为提升用户体验的核心能力之一
  • Qwen3-4B-Thinking-2507-Gemini-2.5-Flash-Distill环境配置详解:MySQL数据库连接与向量存储集成
  • 品牌升级后卖不动,先别怪设计公司
  • 虚拟线程CPU爆表却吞吐不升?深度解析Java 25 Project Loom调度器v2.3内核变更,定位3类隐蔽资源饥饿场景
  • Windows和Office激活终极指南:5分钟搞定KMS智能激活
  • 企业想用AI做数据分析,但数据不能出内网,怎么办
  • 从“找bug”到“质量赋能”:敏捷时代软件测试角色的深度转型
  • 2026年言笔AI去痕:高效消除论文AI痕迹,轻松降低AI率 - 降AI实验室
  • 器官芯片失效分析:面向软件测试从业者的专业视角与工程化方法