说说跨域怎么解决?我:先看我这篇 CORS+Proxy 实战笔记🚀✨

0 阅读6分钟

各位前端er,是不是每次遇到跨域问题,都想把浏览器的 “同源策略” 暴打一顿?明明前后端分离是为了提效,结果跨域报错比产品经理的改需求还频繁!别慌,今天咱就把跨域这事儿掰扯明白。

image.png

一、同源策略:浏览器的 “杠精” 门禁系统

这事儿得从浏览器的 “安全执念” 说起。1995 年 Netscape 搞出的同源策略,规定了仨兄弟(协议、域名、端口)必须一模一样才算 “自己人”,不然就不让互相访问数据。
比如你本地跑的http://localhost:3000想调线上接口https://api.xxx.com,浏览器直接拦下来:“对不起,协议不同,禁止串门!”
内心 OS:知道你是为了安全,但咱前后端分离项目天天跨域,能不能别这么死脑筋?不过想想也是,要是没这限制,隔壁恶意网站能随便偷你 Cookie,那才是真的噩梦...

二、跨域报错现场:控制台的红色死亡警告

当你自信满满写完接口请求,一运行发现控制台飘红:

Access to fetch at 'https://api.xxx.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.  

是不是瞬间血压升高?这就好比你敲开服务器的门,服务器说 “没门牌号(响应头)不让进”,而你的 fetch 请求只能在门口干瞪眼,连catch里的报错信息都拿不到 —— 跨域问题专治各种 “代码写得挺对但就是不跑” 的玄学 bug。

三、CORS:让服务器主动给你开门的正确姿势

别骂浏览器了,真正该改的是服务器!CORS 作为官方认证的 “跨域通行证”,核心就是让服务器返回正确的响应头,告诉浏览器:“这兄弟是自己人,放行!”

(一)三个关键响应头:比产品需求还重要的配置

  1. Access-Control-Allow-Origin

    • 能写具体域名就别偷懒用*,生产环境用*等于 “欢迎所有黑客访问”,分分钟被安全组约谈。
    • 正确操作:http://client.xxx.com,精准授权,只让信任的前端域名访问。
  2. Access-Control-Allow-Methods

    • 遇到PUTDELETE请求报错?大概率是这里没配置。比如后端只写了GET,你却用了POST,服务器直接甩脸:“不支持的方法,爬!”
  3. Access-Control-Allow-Headers

    • 之前帮人调接口就是没加这个头,导致Authorization认证头一直被拦截,差点以为是 JWT 过期 —— 记住,自定义请求头必须在这里声明!

(二)简单请求 vs 预检请求:服务器的 “先礼后兵”

  • 简单请求:像GET、表单提交这种 “老实请求”,浏览器直接发出去,服务器乖乖返回头就行。
  • 预检请求:遇到PUT、带application/jsonPOST这种 “复杂请求”,浏览器先派OPTIONS去探路:“大哥,我能用这个方法和头吗?” 服务器得返回正确的头才算通过 “面试”,不然正式请求根本发不出去。

(三)Node.js 配置实战:一行代码搞定开发环境

// 开发环境图省事?一行代码直接通吃  
app.use(cors());  

// 但生产环境必须严谨!白名单安排上  
const whitelist = ['http://官网域名', 'http://后台管理域名'];  
app.use(cors({ origin: (origin, cb) => whitelist.includes(origin) || cb(new Error('禁止访问')) }));  

划重点:开发环境随便浪,生产环境必须收敛,不然安全审计能让你改到怀疑人生。

四、Proxy:开发摸鱼 & 生产护驾的双面手

(一)开发环境:代理让你假装 “同域” 摸鱼

Vue CLI、Webpack Dev Server 的代理功能简直是摸鱼神器!在vue.config.js里写几行,前端请求自动转发到后端:

proxy: {  
  '/api': {  
    target: 'http://后端域名',  
    changeOrigin: true, // 让后端以为请求来自自己人  
    pathRewrite: { '^/api': '' } // 去掉前缀,假装路径没毛病  
  }  
}  

从此写接口直接/api/user,再也不用记长长的域名,跨域?不存在的,浏览器还以为你在访问本地接口呢~

(二)生产环境:Nginx 反向代理 —— 你的全能保镖

上线后靠 Nginx 撑场子!配置反向代理 + CORS 头,既能隐藏真实后端地址,又能统一处理跨域:

server {  
  listen 80;  
  server_name 你的域名;  

  location / {  
    proxy_pass http://真实后端地址;  
    add_header Access-Control-Allow-Origin http://前端域名;  
    if ($request_method = 'OPTIONS') {  
      return 204; // 预检请求直接秒回,别让浏览器等太久  
    }  
  }  
}  

反向代理简直是生产环境的 “全能保镖”,除了跨域,还能搞负载均衡、缓存静态资源。

五、其他方案:那些年我们踩过的坑

(一)JSONP:上古时代的 “偏门解法”

还记得刚学前端时用的 JSONP 吗?靠<script>标签跨域,回调函数一写,数据就来了。但这玩意儿只能发GET请求,还容易被 XSS 攻击 —— 现在谁还用啊?除非你在维护十年前的老项目,否则建议直接拉黑。

(二)WebSocket:实时通信的 “跨域特种兵”

搞在线聊天、实时监控?WebSocket 直接无视同源策略,全双工通信爽到飞起!但服务器得支持ws协议,前端new WebSocket('ws://xxx.com'),后端用ws库接招,适合对实时性要求高的场景。

(三)iframe+postMessage:页面嵌套的 “跨窗密聊”

如果你在页面里嵌了别的域名的 iframe,想通信就得靠postMessage

// 主窗口给iframe发消息  
iframe.contentWindow.postMessage('数据给你啦', 'https://iframe域名');  

// iframe接消息前先检查来源,别随便收陌生人的消息!  
window.addEventListener('message', (event) => {  
  if (event.origin === 'https://主窗口域名') {  
    console.log('收到消息:', event.data);  
  }  
});  

注意:一定要校验event.origin,不然恶意 iframe 能把你坑到哭。

六、全场景避坑指南:从开发到上线的生存法则

(一)开发期:快速试错,别死磕

  • 先用工具代理绕过跨域,先保证业务逻辑能跑,别一开始就和 CORS 头较劲 —— 反正上线前迟早要改服务器配置。
  • 装个浏览器插件(比如 Moesif),一键修改请求头,快速定位是前端配置问题还是后端没加头。

(二)测试期:把能想到的坑都踩一遍

  • 用 Postman 模拟各种请求:PUT、带自定义头的请求、预检请求,看看服务器返回的头对不对。
  • 让后端同事故意把Access-Control-Allow-Origin写错,看看前端报错是否符合预期,提前发现配置漏洞。

(三)生产期:安全第一,监控到位

  • 千万别用*!千万别用*!千万别用*!重要的事情说三遍,按最小权限原则配置可信域名。
  • Access-Control-Max-Age缓存预检请求,比如86400(一天),减少重复 OPTIONS 请求,提升性能。
  • 监控系统加上跨域错误报警,比如前端用 Sentry 捕获TypeError: Failed to fetch,后端日志统计 OPTIONS 请求频率,发现异常及时排查。