跨域:原因、解决方案CORS、JSONP、proxy、iframe(自用)

一、跨域的根本原因:同源策略

同源策略 ​ 是浏览器最核心的安全基石。它限制了从一个 加载的文档或脚本如何与另一个的资源进行交互。

  • 什么是"同源"? ​ 协议、域名、端口三者必须完全相同。

    • 同源:https://www.example.com/page1https://www.example.com/page2
    • 不同源:https://www.example.comhttp://www.example.com(协议不同)
    • 不同源:https://www.example.comhttps://api.example.com(域名不同)
    • 不同源:https://www.example.comhttps://www.example.com:8080(端口不同)
  • 限制了什么?

    1. DOM 访问:无法用 JS 读取不同源页面的 DOM、Cookie、LocalStorage 等。
    2. Ajax 请求 :默认情况下,无法用 XMLHttpRequestFetch API向不同源的接口发送请求。
    3. 注意 :链接 <a><img><script><link>等标签的 srchref属性可以 加载跨域资源,这是跨域资源共享实现的基础。

"跨域"问题的本质是:浏览器限制了脚本发起的跨域 HTTP 请求的响应数据被 JS 代码读取,但请求实际已发出并到达了服务器。

二、解决方案

1. CORS(跨域资源共享)------ 现代标准解决方案

这是 W3C 标准,由服务端通过设置 HTTP 响应头来告诉浏览器允许哪些源、方法、头部进行跨域访问。

  • 简单请求 :满足特定条件(如方法为 GET/POST/HEAD,Content-Type 为 application/x-www-form-urlencoded, multipart/form-data, text/plain)。

    • 浏览器直接发出请求,并在请求头中自动携带 Origin
    • 服务器响应时,必须在响应头中包含 Access-Control-Allow-Origin: *Access-Control-Allow-Origin: https://your-site.com
  • 预检请求 :不满足简单请求条件(如 Content-Type 为 application/json,或使用了 PUT/DELETE 方法等)。

    1. 浏览器会先自动发送一个 OPTIONS​ 方法的"预检请求"。

    2. 服务器必须响应此预检请求,并返回以下关键头部:

      • Access-Control-Allow-Origin: 允许的源
      • Access-Control-Allow-Methods: 允许的 HTTP 方法
      • Access-Control-Allow-Headers: 允许的自定义请求头
    3. 预检通过后,浏览器才会发送真正的请求。

  • 附带身份凭证的请求:如果请求需要携带 Cookie 或 HTTP 认证信息。

    • 前端:fetch(url, { credentials: 'include' })xhr.withCredentials = true
    • 服务器:响应头必须包含 Access-Control-Allow-Credentials: true,并且 Access-Control-Allow-Origin不能为 *,必须是明确的源。

核心:CORS 的关键在于服务器端的配置。

2. JSONP(JSON with Padding)------ 历史方案,用于 GET 请求

利用 <script>标签没有跨域限制的特性来实现。

  • 原理

    1. 前端定义一个全局回调函数,例如 function handleResponse(data) { ... }
    2. 动态创建一个 <script>标签,其 src指向目标接口,并携带回调函数名作为参数,如 https://api.other.com/data?callback=handleResponse
    3. 服务器接收到请求后,将数据作为参数,包裹在回调函数调用中返回,如 handleResponse({"data": 123})
    4. 浏览器下载并执行此脚本,相当于调用了前端的 handleResponse函数,从而拿到了数据。
  • 缺点

    • 只支持 GET 请求。
    • 安全性差,容易受到 XSS 攻击。
    • 错误处理机制不完善。

3. 代理服务器(Proxy)------ 开发环境常用

在前后端分离的开发中,为了解决本地开发时的跨域问题,常用代理方案。

  • 原理:由于同源策略是浏览器的限制,服务器之间通信没有此限制。可以设置一个同源的代理服务器,前端将所有请求发给这个代理,由代理转发给目标服务器,再将响应返回给前端。

  • 实现

    • 开发环境 :使用 Webpack DevServer 的 proxy配置、Vite 的 server.proxy配置,或 Nginx 反向代理。
    • 生产环境 :通常用 Nginx 反向代理,将 /api路径的请求代理到真正的后端服务器。

4. 基于 iframe 的方案(已过时,了解即可)

主要是利用 document.domain降级、window.postMessagewindow.name等技巧在不同源的 iframe 之间传递消息。这些方案较为复杂,安全性有挑战,在现代 CORS 成为标准后已很少使用。

  • document.domain :仅适用于主域相同、子域不同的场景(如 a.example.comb.example.com),可同时设置 document.domain = 'example.com'来实现同源。
  • postMessage :HTML5 提供的安全跨源通信方法。一个窗口/iframe 可以向另一个窗口发送消息,目标窗口监听 message事件来接收。

总结与选择

方案 原理 优点 缺点 适用场景
CORS 服务端设置响应头 W3C标准,功能强大,支持所有HTTP方法,安全性好 需要服务端配合修改 生产环境首选,前后端分离的标准方案
JSONP <script>标签 兼容性极好,支持老浏览器 仅GET,安全性差,错误处理难 需要支持老旧浏览器,且接口仅支持GET的场景(越来越少)
代理 服务端转发请求 前端无需修改代码,开发环境无缝切换 生产环境需部署代理服务器,增加架构复杂性 开发环境主流方案,或生产环境用于隐藏真实后端地址、负载均衡
iframe 窗口间消息传递 可实现特定复杂场景的跨域通信 实现复杂,安全性需谨慎处理,主方案过时 已逐渐被CORS和postMessage取代,特殊遗留系统兼容

现代 Web 开发的通用实践是:

  • 开发阶段 :使用 Webpack/Vite 代理Nginx 反向代理 来绕过跨域问题。
  • 生产环境 :前后端分离部署,由后端服务正确配置 CORS 响应头 ,或通过 Nginx 反向代理 将 API 请求转发到后端,使前端看起来是在同源下请求。
相关推荐
Cache技术分享4 小时前
346. Java IO API - 操作文件和目录
前端·后端
滕青山4 小时前
HTTP状态查询 在线工具核心JS实现
前端·javascript·vue.js
左右用AI4 小时前
给你的AI员工装个对讲机:3步搞定小龙虾Telegram遥控
前端
忆江南4 小时前
# iOS 电量优化详解
前端
忆江南4 小时前
# iOS weak 原理详解
前端
小码哥_常4 小时前
解锁Android开发封装密码,打造高效代码城堡
前端
在西安放羊的牛油果4 小时前
我把 2000 行下单代码,重构成了一套交易前端架构
前端·设计模式·架构
im_AMBER4 小时前
今日开发反思:编辑器大纲跳转与数据持久化实践
前端·架构