为什么克制我哀求别的网站的接口?——跨域与CORS [复制链接]
发表于 2026-4-9 11:04:25 | 显示全部楼层 |阅读模式
🧑‍💻 写在开头

点赞 + 收藏 === 学会🤣🤣🤣
 
你有没有遇到过这种环境:在本身的网页上想哀求别人的API,效果欣赏器直接报错:Access-Control-Allow-Origin' header is missing。为什么欣赏器要克制你?服务器不相应不就完了吗?
本日,用小区门禁的故事,来讲讲 跨域 与 CORS。
什么是"跨域"?

同源战略 — 欣赏器的安全基石

欣赏器有个同源战略(Same-Origin Policy):只有来自同一个"家"的资源才华任意用。
什么叫"同一个家"?看三个条件:协议(http/https)、域名(example.com)、端口(:8080)。三个都一样,才是同源;有一个不一样,就是跨域。
跨域的例子
  1. ✅ http://example.com 和 http://example.com/profile<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form> // 协议+域名+端口都相同 → 同源
  5. ✅ https://example.com 和 https://example.com<form action="http://bank.com/transfer" method="POST">
  6.   <input type="hidden" name="to" value="hacker">
  7.   <input type="hidden" name="amount" value="1000000">
  8. </form><form action="http://bank.com/transfer" method="POST">
  9.   <input type="hidden" name="to" value="hacker">
  10.   <input type="hidden" name="amount" value="1000000">
  11. </form>   // 协议+域名+端口都相同 → 同源
  12. ❌ http://example.com 和 https://example.com<form action="http://bank.com/transfer" method="POST">
  13.   <input type="hidden" name="to" value="hacker">
  14.   <input type="hidden" name="amount" value="1000000">
  15. </form><form action="http://bank.com/transfer" method="POST">
  16.   <input type="hidden" name="to" value="hacker">
  17.   <input type="hidden" name="amount" value="1000000">
  18. </form>   // 协议不同 → 跨域
  19. ❌ http://example.com 和 http://api.example.com<form action="http://bank.com/transfer" method="POST">
  20.   <input type="hidden" name="to" value="hacker">
  21.   <input type="hidden" name="amount" value="1000000">
  22. </form><form action="http://bank.com/transfer" method="POST">
  23.   <input type="hidden" name="to" value="hacker">
  24.   <input type="hidden" name="amount" value="1000000">
  25. </form>// 域名不同(子域名)→ 跨域
  26. ❌ http://example.com:8080 和 http://example.com:3000  // 端口不同 → 跨域
复制代码
跨域限定了什么?

欣赏器的同源战略告急限定了三件事:

  • DOM 访问:无法读取差别源的 iframe 内容、无法修改差别源的 iframe DOM
  • AJAX 哀求:无法哀求差别源的 API
  • Cookie/LocalStorage:无法访问差别源的数据
为什么要限定跨域?

模拟一个攻击场景

想象一下:你登录了银行网站 bank.com,欣赏器生存了你的登录 Cookie。
然后你手滑点进了一个恶意网站 evil.com,这个网站里有一段代码:
  1. <form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form>
复制代码
如果没有同源战略,这个表单哀求会自动带上 bank.com 的 Cookie,银行服务器以为是你本人操纵的——钱就没了。
同源战略就是欣赏器的"门禁":只有同一家人才华进,陌生人要查证件。
💡 注意:<img> 标签的 GET 哀求固然也会带 Cookie,但当代欣赏器有 SameSite Cookie 掩护。上面表单 POST 场景更范例。
CORS — 跨域的"通行证"

CORS 是什么?

CORS(Cross-Origin Resource Sharing)= 跨域资源共享。
它的工作原理很简朴:让服务器告诉欣赏器,"我允许来自这些源的哀求"
简朴哀求 vs 预检哀求

简朴哀求

满足以下条件的哀求是"简朴哀求":

 简朴哀求的流程:
  1. 1. 浏览器发送请求(自动带上 Origin 头)
  2.    ↓
  3. 2. 服务器检查 Origin,决定是否允许
  4.    ↓
  5. 3. 服务器返回响应头 Access-Control-Allow-Origin
  6.    ↓
  7. 4. 浏览器检查响应头,允许就完事
复制代码
服务器端示例(Node.js):
  1. app.get('/api/data', (req, res) => {  const origin = req.headers.origin;  if (origin === 'https://example.com') {<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form>res.setHeader('Access-Control-Allow-Origin', origin);  }  res.json({ data: '这是返回的数据' });});
复制代码
相应头:
  1. HTTP/1.1 200 OK
  2. Access-Control-Allow-Origin: https://example.com
  3. Content-Type: application/json
  4. {"data": "这是返回的数据"}
复制代码
预检哀求(Preflight)

不满足"简朴哀求"条件的,欣赏器会先发一个 OPTIONS 哀求"探路":
  1. 1. 浏览器发送 OPTIONS 预检请求
  2.    ↓
  3. 2. 服务器检查方法/头部/Origin
  4.    ↓
  5. 3. 服务器返回允许的头 Access-Control-*
  6.    ↓
  7. 4. 浏览器发送实际请求
复制代码
预检哀求查抄什么?
预检哀求(OPTIONS)就像登机前的安检——先查抄你带没带伤害品。
欣赏器会问服务器三件事:

  • 我从哪来?(Origin)
  • 我想用什么方法?(Access-Control-Request-Method)
  • 我想带什么头?(Access-Control-Request-Headers)
服务器答复"可以",欣赏器才放行实际哀求。
  1. # 哀求(欣赏器发给服务器)OPTIONS /api/data HTTP/1.1Origin: https://example.com<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form><form action="http://bank.com/transfer" method="POST">
  5.   <input type="hidden" name="to" value="hacker">
  6.   <input type="hidden" name="amount" value="1000000">
  7. </form><form action="http://bank.com/transfer" method="POST">
  8.   <input type="hidden" name="to" value="hacker">
  9.   <input type="hidden" name="amount" value="1000000">
  10. </form>  # 我从哪来Access-Control-Request-Method: PUT<form action="http://bank.com/transfer" method="POST">
  11.   <input type="hidden" name="to" value="hacker">
  12.   <input type="hidden" name="amount" value="1000000">
  13. </form><form action="http://bank.com/transfer" method="POST">
  14.   <input type="hidden" name="to" value="hacker">
  15.   <input type="hidden" name="amount" value="1000000">
  16. </form># 我想用 PUT 方法Access-Control-Request-Headers: Content-Type, Authorization  # 我想带这些头---# 相应(服务器告诉欣赏器)HTTP/1.1 204 No ContentAccess-Control-Allow-Origin: https://example.com  # 允许这个源Access-Control-Allow-Methods: GET, POST, PUT, DELETE  # 允许这些方法Access-Control-Allow-Headers: Content-Type, Authorization  # 允许这些头Access-Control-Max-Age: 86400<form action="http://bank.com/transfer" method="POST">
  17.   <input type="hidden" name="to" value="hacker">
  18.   <input type="hidden" name="amount" value="1000000">
  19. </form><form action="http://bank.com/transfer" method="POST">
  20.   <input type="hidden" name="to" value="hacker">
  21.   <input type="hidden" name="amount" value="1000000">
  22. </form>  # 预检效果缓存24小时
复制代码
服务器端处理处罚:
  1. app.options('/api/data', (req, res) => {  const origin = req.headers.origin;  if (origin === 'https://example.com') {<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form>res.setHeader('Access-Control-Allow-Origin', origin);<form action="http://bank.com/transfer" method="POST">
  5.   <input type="hidden" name="to" value="hacker">
  6.   <input type="hidden" name="amount" value="1000000">
  7. </form>res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');<form action="http://bank.com/transfer" method="POST">
  8.   <input type="hidden" name="to" value="hacker">
  9.   <input type="hidden" name="amount" value="1000000">
  10. </form>res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');<form action="http://bank.com/transfer" method="POST">
  11.   <input type="hidden" name="to" value="hacker">
  12.   <input type="hidden" name="amount" value="1000000">
  13. </form>res.setHeader('Access-Control-Max-Age', '86400');  }  res.status(204).send();});
复制代码
CORS 相应头详解

常用相应头


credentials 模式

默认环境下,CORS 不带 Cookie。如果须要携带 Cookie:
前端:
  1. fetch('/api/data', {
  2.   credentials: 'include'
  3. });
复制代码
服务端:
  1. res.setHeader('Access-Control-Allow-Origin', 'https://example.com');
  2. res.setHeader('Access-Control-Allow-Credentials', 'true');
复制代码
注意:Access-Control-Allow-Origin 不能用 *,必须是详细域名。
跨域的办理方案

1. JSONP(已不保举)


使用 [/code]

2. 署理服务器

在本身的服务器上转发哀求,"伪装"成同源:
  1. 浏览器 ──> 我的服务器(同一源) ──> 目标服务器
复制代码
Nginx 署理:
  1. location /api/ {
  2.   proxy_pass http://target-server.com/;
  3. }
复制代码
Node.js 署理:
  1. app.get('/api/data', async (req, res) => {
  2.   const response = await fetch('http://target-server.com/data');
  3.   const data = await response.json();
  4.   res.json(data);
  5. });
复制代码
3. Webpack/Vite 开辟署理

开辟环境设置署理:
  1. // vite.config.jsexport default {  server: {<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form>proxy: {<form action="http://bank.com/transfer" method="POST">
  5.   <input type="hidden" name="to" value="hacker">
  6.   <input type="hidden" name="amount" value="1000000">
  7. </form>  '/api': {<form action="http://bank.com/transfer" method="POST">
  8.   <input type="hidden" name="to" value="hacker">
  9.   <input type="hidden" name="amount" value="1000000">
  10. </form><form action="http://bank.com/transfer" method="POST">
  11.   <input type="hidden" name="to" value="hacker">
  12.   <input type="hidden" name="amount" value="1000000">
  13. </form>target: 'http://target-server.com',<form action="http://bank.com/transfer" method="POST">
  14.   <input type="hidden" name="to" value="hacker">
  15.   <input type="hidden" name="amount" value="1000000">
  16. </form><form action="http://bank.com/transfer" method="POST">
  17.   <input type="hidden" name="to" value="hacker">
  18.   <input type="hidden" name="amount" value="1000000">
  19. </form>changeOrigin: true<form action="http://bank.com/transfer" method="POST">
  20.   <input type="hidden" name="to" value="hacker">
  21.   <input type="hidden" name="amount" value="1000000">
  22. </form>  }<form action="http://bank.com/transfer" method="POST">
  23.   <input type="hidden" name="to" value="hacker">
  24.   <input type="hidden" name="amount" value="1000000">
  25. </form>}  }};
复制代码
4. postMessage

差别学口/iframe 之间的通讯:
  1. window.addEventListener('message', (event) => {  if (event.origin === 'https://example.com') {<form action="http://bank.com/transfer" method="POST">
  2.   <input type="hidden" name="to" value="hacker">
  3.   <input type="hidden" name="amount" value="1000000">
  4. </form>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 不匹配),处理处罚方式不一样的。
如果对您有所资助,欢迎您点个关注,我会定时更新技能文档,各人一起讨论学习,一起进步。



免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表