一、什么是同源策略(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(跨站请求伪造)攻击。
一个典型的攻击场景:
- 你登录了银行网站
bank.com,浏览器中保存了登录凭证(Cookie)。 - 你不小心打开了恶意网站
hack.com。 - 如果没有同源限制,
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
整个流程如下:
-
浏览器 JS 发起 AJAX / Fetch 请求访问后端接口
http://127.0.0.1:8080/api/login。 -
请求顺利到达后端,后端正常执行业务逻辑并返回数据。
-
后端在响应中附带如下响应头:
Access-Control-Allow-Origin: http://127.0.0.1:5173 -
响应数据包返回到浏览器。
-
浏览器执行校验:
- 当前页面源:
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 方法跨域访问。
默认情况下,浏览器只允许跨域请求使用三种"安全"方法:GET、POST、HEAD(简单请求)。
如果你的前端要发 PUT、DELETE、PATCH 等请求,浏览器会先发一个 预检请求 (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等
只要请求头超出了浏览器默认的"简单头"(如 Accept、Accept-Language、Content-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 })或fetch的credentials: 'include')
⚠️ 重要限制 :当
credentials为true时,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-Methods、Allow-Headers、Allow-Credentials分别控制请求方法、自定义头和凭证携带。
理解了这些原理,就能更好地配置后端 CORS 策略,解决开发中常见的跨域问题。