跨域请求笔记

同源策略 :域名、协议、端口完全相同则称为同源

跨域:请求的域名、协议、端口其中之一不同的情况下发生跨域

跨域解决

JSONP(JSON with Padding)

JSON with Padding:把 JSON 数据「填充」到函数中。

动态创建 <script> 去发送参数。

  • 利用了 <script>的标签本身的跨域特性,同时 <script> 标签中的代码会自动执行
  • 通过 ?callback=<函数名> 指明回调函数
  • 服务端返回包裹 JSON 数据的回调函数执行代码来触发执行

客户端

javascript 复制代码
// 1. 定义回调函数(服务端返回的代码会调用此函数)
function jsonpCallback(data) {
  console.log('跨域数据:', data); // { name: '张三', age: 20 }
}

// 2. 动态创建 script 标签,发起请求
const script = document.createElement('script');
// 接口地址 + 回调函数名(服务器需识别 callback 参数)
script.src = 'https://api.other.com/data?callback=jsonpCallback';
document.body.appendChild(script);

// 3. 可选:请求完成后移除 script 标签
script.onload = () => document.body.removeChild(script);

服务端

javascript 复制代码
app.get('/data', (req, res) => {
  const callbackName = req.query.callback; // 获取前端传入的回调函数名
  const data = { name: '张三', age: 20 }; // 要返回的数据
  // 返回格式:回调函数名(JSON数据)
  res.send(`${callbackName}(${JSON.stringify(data)})`);
});

局限性

  • JSONP 仅支持 GET 请求,无法处理复杂数据。
  • 内容暴露链接,有可能会导致数据被记录到日志系统中,存在泄密风险。

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

  • 在受控条件下允许跨域资源请求。
  • CORS 仅对 fetch/XHR 生效,对 <script><link><img><iframe> 等标签不生效。
  • 跨域请求时浏览器会先发 OPTIONS 预检请求,服务端通过 CORS 响应头声明是否允许跨域。
简单请求

不会触发 OPTIONS 预检请求,需同时满足下列条件。

  • 请求方法GETPOSTHEAD 三种之一。

  • 头部限制:不能包含自定义请求头。

  • 类型限制Content-Type 仅允许 application/x-www-form-urlencodedmultipart/form-datatext/plain

  • 请求中没有 ReadableStream 对象。

  • 请求中 XMLHttpRequestUpload 未注册监听器。

简单请求下,服务端需在响应中返回的响应头

  • Access-Control-Allow-Origin(必须) :允许发起跨域请求的源;携带凭证时不能设为 *
预检请求

上述条件不满足,就是非简单请求,此时会触发 OPTIONS 预检请求。

OPTIONS 预检请求是实际请求前的 HTTP 请求,用于获知服务端是否允许实际请求发送。

常见预检请求请求头(非简单请求时由浏览器自动添加)

  • Access-Control-Request-Method:声明将使用的请求方法,必须携带。
  • Access-Control-Request-Headers:指定会额外发送的请求头字段,逗号分隔。

预检响应中需携带的响应头

  • Access-Control-Allow-Methods(必须) :与 Access-Control-Request-Method 对应。
  • Access-Control-Allow-Headers(必须) :与 Access-Control-Request-Headers 对应,自定义请求头时必设。
  • Access-Control-Max-Age:预检结果缓存时间;未设置时多为 5s,最大可设为 86400(1 天)。

其他响应头(简单请求与预检响应均可使用)

  • Access-Control-Expose-Headers :设置可暴露给 JS 脚本获取的响应头;设为 * 时不会带上凭证信息。

  • Access-Control-Allow-Credentials :设为 true 表示允许跨域请求携带 Cookie 等凭证。

    • 需配合客户端的 xhr.withCredentials = true 使用(axios 中对应 withCredentials)。
    • 此时 Access-Control-Allow-Origin 不能为 *,否则请求会失败。
执行流程

1. 浏览器发起跨域请求

浏览器判断是否为简单请求,以及是否需要预检请求。

2. 简单请求处理

  1. 浏览器直接发送实际请求。
  2. 服务端处理请求并返回响应。
  3. 浏览器检查响应头:是否存在 Access-Control-Allow-Origin;若携带凭证,还需校验 Access-Control-Allow-Credentials
  4. 校验通过后可读取响应,否则报 CORS 错误(请求已到达,被浏览器拦截)。

3. 非简单请求(预检请求)处理

  1. 发送 OPTIONS 预检请求,携带 Access-Control-Request-MethodAccess-Control-Request-Headers 询问服务端许可。
  2. 服务端响应 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-HeadersAccess-Control-Max-Age
  3. 校验通过后继续发送实际请求(即 OPTIONS 之后的请求);校验失败则直接拦截,不发送实际请求。
  4. 实际请求发出后,浏览器再次校验响应头,逻辑同简单请求。
  5. 若携带 Cookie/Authorization(withCredentials = true),服务端需返回 Access-Control-Allow-Credentials: true

CORB(Cross-Origin Read Blocking)跨域读阻塞

CORB(Cross-Origin Read Blocking)跨域读阻塞是浏览器的一种安全机制,用于阻止跨域响应被恶意读取。

触发条件

  • 请求源与响应源不一致。
  • 响应 Content-Typetext/htmlapplication/jsonapplication/xml
  • 响应头未设置或不匹配 Access-Control-Allow-Origin
  • 通过 XHR、fetch 等可读取响应体的请求。

错误消息提示

CORB blocked cross-origin response

若是 fetch/XHR 请求,可能看到类似 fetch at 'xxx' from origin 'xxx' has been blocked by CORS policy 的 CORS 报错。

可查看开发者工具中 Network 面板:请求已发出但被拦截多为 CORB;请求未发出则多为 CORS 预检或策略限制。

与 CORS 的关系

  • CORS 是浏览器对跨域请求的授权机制。
  • CORB 是浏览器在渲染进程层对高风险响应的读阻塞机制,仅拦截可被 JS 解析的敏感类型响应。

Nginx 跨域代理配置

  • 使用 Access-Control-Allow-Origin 且需携带 Cookie 时,必须指定具体域名。

  • 可用子域名管理接口,例如 api.xxx.com

  • 使用 map 统一管理允许的域名列表,语法形如:map $var1 $var2 { default xxx; ... }

    • $var1:输入,一般为 Nginx 内置变量(如 $http_origin)。

    • $var2:输出,由 map 中 key-value 映射得到。

    • { default xxx; ... }default 为默认值,匹配不到时使用该值;否则按后续 key value 映射,当 $var1 匹配到某 key 时,$var2 取对应 value

注意Access-Control-Allow-Origin 在需要携带 Cookie 时必须指定具体域名(不能为 *)。

nginx 复制代码
server {
  listen 80;
  server_name api.xxx.com; # 后端接口域名(建议单独用 api 子域名)

  # 允许的前端域名列表(用 map 统一管理,便于维护)
  map $http_origin $allow_origin {
    default ""; # 默认拒绝
    "~^https?://(a\.xxx\.com|b\.xxx\.com|www\.xxx\.com)$" $http_origin; # 允许的域名(支持 http/https)
  }

  location / {
    # 跨域响应头(按 Origin 动态匹配允许的域名)
    add_header Access-Control-Allow-Origin $allow_origin always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always; # 支持全量常用方法
    add_header Access-Control-Allow-Headers "Content-Type, Authorization, Token, X-Requested-With" always; # 补充 Token/Authorization(前后端鉴权常用)
    add_header Access-Control-Allow-Credentials "true" always;
    add_header Access-Control-Max-Age "86400" always; # 缓存 24 小时(减少预检请求)
    add_header Vary "Origin" always; # 配合多域名,避免浏览器缓存冲突

    # 处理预检请求
    if ($request_method = 'OPTIONS') {
      return 204;
    }

    # 后端代理配置(生产环境建议加超时和缓冲区配置)
    proxy_pass http://backend_service; # 后端服务(可配置 upstream 集群)
    proxy_connect_timeout 10s;
    proxy_read_timeout 30s;
    proxy_buffer_size 4k;
    proxy_buffers 4 16k;
    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;
  }
}

其他

父子页面跨域通信 :可基于 <iframe> 与 postMessage API 实现;需注意避免响应头 X-Frame-Options: SAMEORIGIN 导致子页面无法被嵌入。

本地开发:可通过本地代理规避跨域,例如 webpack-dev-server、Vite 的 dev server proxy 均以代理方式转发请求。

electron 中微前端跨域场景 :在 eletron 客户端内编译渲染 HTML 页面(不是访问)实现多项目的微前端,项目内部控制发起项目域名的请求,此时请求协议可能为 app:// ,就会导致跨域,需要项目服务端放开对应协议来支持跨域请求。

总结

  • 同源:协议 + 域名 + 端口一致;否则算跨域。
  • JSONP:靠 script 跨域 + callback 名,只支持 GET,现在很少用。
  • CORS :服务端加响应头放行;简单请求直接带 Access-Control-Allow-Origin,非简单请求先走 OPTIONS 预检,再发实际请求。带 Cookie 时不能写 *,要写具体域名。
  • CORB:浏览器拦「跨源 + 敏感 Content-Type + 无/错 CORS 头」的响应,避免被 JS 读到。报错像 CORS 时看 Network:已发出被拦多半是 CORB,没发出多半是 CORS。
相关推荐
tritone3 小时前
使用阿贝云免费云服务器学习Vagrant,是一次非常顺畅的体验。作为一名开发者
服务器·学习·vagrant
2501_901147833 小时前
面试必看:优势洗牌
笔记·学习·算法·面试·职场和发展
37方寸3 小时前
前端基础知识(Node.js)
前端·node.js
小白电脑技术3 小时前
飞牛漏洞焦虑?别瞎折腾WAF了!用Lucky五步搞定“防爬墙”
服务器·网络·安全
tzy2333 小时前
极简版本的 TCP / IP 协议栈介绍
网络·网络协议·tcp/ip
一战成名9963 小时前
深度解析 CANN 模型转换工具链:从 ONNX 到 OM
人工智能·学习·安全·开源
tod1133 小时前
TCP全连接队列与tcpdump抓包
网络·网络协议·tcp/ip·github·tcpdump
cheems95273 小时前
【javaEE】全方位拆解 UDP 协议
网络·网络协议·udp
powerfulhell3 小时前
寒假python作业5
java·前端·python