浏览器同源策略与 CORS 跨域详解

一、什么是同源策略(Same-Origin Policy)

浏览器有一个核心安全机制------同源策略 。它规定了只有同源的页面之间,才能正常互相请求资源;非同源的请求,浏览器会拦截 ,这种现象就是我们常说的跨域

1. 同源判断的三个条件

必须同时满足以下三个条件,才属于同源:

  • 协议相同
  • 域名(IP)相同
  • 端口相同

任意一个不一样,都属于跨域。

2. 举例说明

假设当前页面地址为:http://localhost:8080/index.html

请求地址 是否跨域 不同点
http://localhost:8080/user ❌ 不跨域 三者完全一致
https://localhost:8080/user ✅ 跨域 协议 http ≠ https
http://127.0.0.1:8080/user ✅ 跨域 域名 localhost ≠ 127.0.0.1
http://localhost:8081/user ✅ 跨域 端口 8080 ≠ 8081
http://api.baidu.com ✅ 跨域 域名不同

⚠️ 注意

同源策略限制的是浏览器发起的 AJAX / Fetch 请求

<img><script><link> 等标签加载资源不受同源限制(这也是为什么我们可以直接引用第三方 CDN 的图片、JS 库等)。

二、为什么浏览器要搞同源策略?

核心目的:防止 CSRF(跨站请求伪造)攻击

一个典型的攻击场景:

  1. 你登录了银行网站 bank.com,浏览器中保存了登录凭证(Cookie)。
  2. 你不小心打开了恶意网站 hack.com
  3. 如果没有同源限制,hack.com 页面可以偷偷发起一个 AJAX 请求到 bank.com/transfer,浏览器会自动带上你的银行 Cookie,从而在你不经意间完成转账操作。

同源策略通过阻止恶意页面的 AJAX 请求,有效保护了用户数据的安全。

三、跨域是如何被拦截的?

跨域的拦截是由浏览器执行的,而不是后端。

浏览器在收到后端返回的数据后,会检查响应头中的 Access-Control-Allow-Origin 字段,判断是否放行。

完整时序示例

假设我们有以下两个地址:

  • 前端页面:http://127.0.0.1:5173(Vue 开发服务器)
  • 后端接口:http://127.0.0.1:8080/api/login

整个流程如下:

  1. 浏览器 JS 发起 AJAX / Fetch 请求访问后端接口 http://127.0.0.1:8080/api/login

  2. 请求顺利到达后端,后端正常执行业务逻辑并返回数据

  3. 后端在响应中附带如下响应头:

    复制代码
    Access-Control-Allow-Origin: http://127.0.0.1:5173
  4. 响应数据包返回到浏览器。

  5. 浏览器执行校验:

    • 当前页面源:http://127.0.0.1:5173
    • 响应头允许的源:http://127.0.0.1:5173
    • ✅ 匹配成功 → 将返回的数据交给前端 JS 使用。

如果没有设置该响应头

即使后端正常返回了数据,浏览器在比对时找不到 Access-Control-Allow-Origin 头,会认为这是一个不被允许的跨域请求,从而拦截响应,JS 无法获取到返回结果,并在控制台输出典型的 CORS 跨域错误。

四、常用的 CORS 响应头详解

除了 Access-Control-Allow-Origin,还有几个配套的响应头常常一起出现。它们控制着跨域请求的更多细节,下面逐一说明。

1. Access-Control-Allow-Methods

作用:告诉浏览器,服务器允许哪些 HTTP 方法跨域访问。

默认情况下,浏览器只允许跨域请求使用三种"安全"方法:GETPOSTHEAD(简单请求)。

如果你的前端要发 PUTDELETEPATCH 等请求,浏览器会先发一个 预检请求 (OPTIONS),询问服务器:"我能用 DELETE 吗?"

服务器在预检响应中返回这个头,明确允许哪些方法,浏览器才会正式发送实际请求。

如果没有这个头,或者其中不包含前端使用的方法(比如 DELETE),那么该请求就会被浏览器直接拦截,根本到不了服务器。

示例:

复制代码
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

2. Access-Control-Allow-Headers

作用:告诉浏览器,服务器允许携带哪些自定义的请求头。

前端有时需要在请求中额外传递信息,例如:

  • Content-Type: application/json(axios 默认的 POST 格式)
  • token(自己定义的身份验证字段)
  • X-Requested-With

只要请求头超出了浏览器默认的"简单头"(如 AcceptAccept-LanguageContent-Type 的特定值),浏览器同样会先发送预检请求,询问:"我能带上 token 这个头吗?"

服务器必须在响应中明确列出允许的自定义头,浏览器才会放行。

如果服务器没写 Access-Control-Allow-Headers: token,那么前端在请求中带上 token 头就会被拦截。

示例:

复制代码
Access-Control-Allow-Headers: Content-Type, token

这里表示允许携带 Content-Type(通常指 application/json)和自定义的 token 头。

3. Access-Control-Allow-Credentials

作用:是否允许跨域请求携带 Cookie 或 HTTP 认证信息。

默认情况下,跨域的 AJAX 请求不会自动附带 Cookie ,即使浏览器中已有对应 Cookie。

如果需要跨域请求也能把 Cookie(比如登录凭证)带过去,前后端必须同时配置:

  • 后端设置 Access-Control-Allow-Credentials: true
  • 前端设置 withCredentials: true(如 axios({ withCredentials: true })fetchcredentials: 'include'

⚠️ 重要限制 :当 credentialstrue 时,Access-Control-Allow-Origin 不能是星号 *,必须指定具体源,例如 http://127.0.0.1:5173

示例:

复制代码
Access-Control-Allow-Origin: http://127.0.0.1:5173
Access-Control-Allow-Credentials: true

这样前端跨域请求才能携带该源下的 Cookie,并且浏览器才会把带 Cookie 的响应数据交给 JS。

常见组合配置

假设你使用 Vue 前端(http://localhost:5173)调用后端接口(http://localhost:8080/api/user),请求方法是 DELETE,并需要在请求头中携带 token,同时还要带上 Cookie。那么后端需要设置如下响应头:

复制代码
Access-Control-Allow-Origin: http://localhost:5173
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, token
Access-Control-Allow-Credentials: true

这样浏览器才能顺利通过预检,并正常执行跨域请求、携带 Cookie。

五、总结

  • 同源策略是浏览器的安全基石,通过协议、域名、端口三要素判断是否同源。
  • 跨域限制主要针对 AJAX / Fetch 请求,但资源性标签(img、script、link)不受影响。
  • 跨域拦截发生在浏览器端,后端可以正常处理请求,但浏览器会检验响应头决定是否放行。
  • CORS(跨域资源共享)是通过设置一系列 Access-Control-* 响应头来安全实现跨域数据交互,其中 Allow-MethodsAllow-HeadersAllow-Credentials 分别控制请求方法、自定义头和凭证携带。

理解了这些原理,就能更好地配置后端 CORS 策略,解决开发中常见的跨域问题。

相关推荐
介一安全1 个月前
【SRC】基础思路篇17:CORS跨域安全测试完全指南
漏洞挖掘·安全性测试·cors
布兰妮甜2 个月前
跨域全方案对比:CORS、Nginx 反向代理、JSONP、iframe、postMessage
nginx·跨域·cors·前端架构·浏览器安全
一棵树73512 个月前
跨域资源共享cors
http·cors
曲幽3 个月前
你的REST接口还在“过度投喂”数据吗?——FastAPI + GraphQL实战避坑指南
python·fastapi·web·graphql·route·cors·rest·strawberry
We་ct4 个月前
深度剖析浏览器跨域问题
开发语言·前端·浏览器·跨域·cors·同源·浏览器跨域
Tisfy5 个月前
CORS 跨域重定向后 Origin 变 null —— 一次 Nginx 字体加载失败的排查记录
运维·nginx·html·cors
csdn_aspnet5 个月前
如何保护您的 .NET Web API 免受常见安全威胁
安全·xss·csrf·.net core·cors
csdn_aspnet5 个月前
在 .NET Core 8 中实现 CORS
.netcore·跨域·cors·.net8
曲幽5 个月前
FastAPI + Vue 前后端分离实战:我的项目结构“避坑指南”
python·vue·fastapi·web·vite·proxy·cors·env