同源策略与跨域,这俩词但凡做过前后端分离开发的人都绕不开。你兴高采烈地调接口,浏览器一盆冷水浇下来:“No 'Access-Control-Allow-Origin' header is present”,那一刻的绝望,我懂。这篇就聊聊同源策略到底是怎么一回事,CORS、JSONP、代理转发这些跨域方案分别解决什么问题,以及我在实际项目里踩过的那些坑。不管你是刚入门的前端,还是被跨域问题找上门的后端,这篇应该都能给你点实在的东西。
1. 同源策略到底在保护谁
要搞懂跨域,得先搞懂为什么会有跨域这回事。浏览器从诞生那天起,就面临一个安全悖论:它既要帮你从互联网上加载各种资源,又得防止恶意网站偷走你的数据。同源策略就是浏览器为了平衡这个矛盾定下的规矩。
1.1 同源的判断规则
同源,指协议、域名、端口三者完全一致。注意是“完全一致”,任何一个不同都算跨域。
| 页面地址 | 请求地址 | 是否同源 | 原因 |
|---|---|---|---|
| https://a.com/page | https://a.com/api | 同源 | 协议域名端口全一致 |
| https://a.com/page | http://a.com/api | 跨域 | 协议不同(https vs http) |
| https://a.com/page | https://b.com/api | 跨域 | 域名不同 |
| https://a.com/page | https://a.com:8443/api | 跨域 | 端口不同 |
这里有个容易忽略的点:默认端口可以省略。https 默认 443,http 默认 80,所以 https://a.com 和 https://a.com:443 是同一个源。但如果你把接口跑在 8080、3000 这种端口上,跟默认端口一比,就算跨域。
还有更隐蔽的情况:一级域名相同不代表同源。比如 a.example.com 和 b.example.com,看着像一家人,但浏览器判定它们是不同的源。想让它俩互通,要么改 document.domain(有严格限制,现在基本不推荐用),要么在后端配置 CORS,要么用代理。这个在后面章节详细展开。
1.2 浏览器到底拦截了什么
这是整个同源策略里最值得琢磨的一点。很多人以为同源策略是服务器在拦截,其实不是。请求该发出去的还是发出去了,服务器该处理的也处理了,响应也正常返回了,是浏览器在拿到响应之后,发现这个响应来自一个不同源的地址,而响应头里又没有允许跨域的声明,于是浏览器把响应“扣下”了,不交给页面里的 JavaScript 代码。
用大白话说:服务器根本不认识你这个浏览器是谁,它是无状态的。浏览器作为中间人,替你把关,发现页面和接口不同源,就把响应体藏起来了,然后在控制台给你报一个 CORS 错误。
理解了这一点,你就明白一个经典现象:直接用 curl 或者 Postman 去请求那个接口,响应是正常的,一点跨域错误都没有。因为 curl 不是浏览器,它没有强制执行同源策略。同源策略是浏览器层面的安全机制,不是服务器的,也不是 HTTP 协议的。前几年我在一个项目里排查问题,后端同事一口咬定接口没问题,非说是我们前端调错了,我让他用 curl 试了一下,接口确实通。但浏览器就是报跨域。原因就是上面说的,服务器不知道请求来自哪个页面,真正做拦截的是浏览器。这个“锅”到底算谁的,得从架构层面看,而不能只看接口本身通不通。
同源策略也不是什么都拦。拿 、