1. 什么是跨域?
先说个故事。
假设你开了家小店,店里有一只看门狗(浏览器的同源策略 )。这只狗的工作很简单:只让本店员工进出,不让陌生人乱窜。但问题来了,你家进货的小哥(前端请求 API)有一天要去隔壁仓库(后端服务器)搬货,结果狗子直接给他拦住了,说:"你不是店里的人,不能去。"
这就是跨域问题的本质------同源策略在作怪。
1.1 这只"狗"是怎么认人的?
浏览器判断"是不是自己人"看的是协议(protocol)、域名(host)、端口号(port) ,只要这三个有任何不同,就会被认定为"外人",触发跨域问题。
URL 1 | URL 2 | 是否同源 |
---|---|---|
http://shop.com |
http://shop.com |
✅ 是同源 |
http://shop.com |
https://shop.com |
❌ 不同协议 |
http://shop.com |
http://api.shop.com |
❌ 不同域名 |
http://shop.com |
http://shop.com:8080 |
❌ 不同端口 |
所以,哪怕你只是改了个 http
为 https
,或者换了个二级域名,浏览器都会大叫:"陌生请求,不要靠近!"
2. 哪些情况会触发跨域?
跨域问题可不仅仅是 fetch
不能用,下面这几种情况,你肯定遇到过:
2.1 AJAX 请求跨域
如果你在 http://shop.com
里用 fetch
请求 http://api.shop.com
,会发生什么?
ini
fetch("http://api.shop.com/data")
.then(response => response.json())
.then(data => console.log(data));
结果: 浏览器直接报 CORS policy error
,请求被拦截,凉凉。
2.2 iframe 访问跨域内容
如果你在 shop.com
里嵌套了一个 other.com
的 iframe
,然后试图用 JavaScript 访问它的 document
:
dart
document.querySelector("iframe").contentWindow.document;
结果: DOMException: Permission denied
,你被告知"权限不够,禁止访问"。
2.3 跨域读取 Cookie
如果 shop.com
想访问 api.shop.com
的 document.cookie
,但 api.shop.com
没有特别配置:
javascript
console.log(document.cookie);
结果: 你得到一个空字符串,因为浏览器默认不允许跨域共享 Cookie。
3. 怎么解决跨域问题?
既然这只看门狗这么严格,我们就得想点"妙招"绕过它。以下是几种常见的跨域解决方案,每一种都有各自的适用场景和坑点。
3.1 CORS(跨域资源共享)------ 官方许可的方式
怎么用?
CORS(Cross-Origin Resource Sharing)是服务器端 的解决方案,意思是:让后端主动告诉浏览器"这个请求我允许" 。
后端返回的 HTTP 头需要加上:
makefile
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type
如果 Access-Control-Allow-Origin
设为 *
,那任何网站都可以访问这个 API。
但如果你要限制访问来源,就需要这样写:
arduino
Access-Control-Allow-Origin: https://shop.com
这样,只有 https://shop.com
才能正常请求数据,其他域名访问依然会被拒绝。
适用场景:
- 前后端分离项目
- 你能控制 API 服务器的配置
坑点:
- 预检请求(
OPTIONS
请求)可能增加请求耗时 Access-Control-Allow-Origin: *
有安全隐患
3.2 JSONP(仅适用于 GET 请求)
怎么用?
JSONP(JSON with Padding)是前端的骚操作 ,通过 <script>
标签加载远程数据:
xml
<script src="https://api.shop.com/data?callback=handleResponse"></script>
然后服务器返回这样的数据:
css
handleResponse({ message: "Hello, JSONP!" });
浏览器执行后,你就能拿到数据了。
适用场景:
- 旧的 API
- 你只需要
GET
请求
坑点:
- 只能用 GET 请求,POST、PUT、DELETE 全部不行
- 容易被 XSS 攻击,因为你直接执行了远程 JavaScript 代码
3.3 反向代理(Nginx)------ 最靠谱的方式之一
怎么用?
反向代理的核心思想是:让前端以为它请求的是同源地址,实际由服务器偷偷代理到跨域服务器。
在 Nginx 里,你可以这么配置:
bash
location /api/ {
proxy_pass http://backend.shop.com/;
}
然后,前端请求 http://shop.com/api/data
,Nginx 会帮你偷偷请求 http://backend.shop.com/data
,绕过浏览器的跨域检查。
适用场景:
- 生产环境
- 你有 Nginx 服务器
坑点:
- 需要服务器支持
- 可能影响性能
3.4 postMessage(适用于 iframe 跨域通信)
怎么用?
如果你有一个 iframe
,而它的内容是跨域的,你可以用 postMessage
进行跨域通信:
javascript
window.parent.postMessage("Hello from iframe", "http://shop.com");
// 在父页面里:
window.addEventListener("message", event => {
if (event.origin === "http://other.com") {
console.log(event.data);
}
});
适用场景:
- iframe 跨域通信
- 嵌套网页之间的数据传递
坑点:
- 需要双方配合
- 必须校验
origin
,否则有安全风险
4. 总结
跨域问题是日常开发绕不开的坑,但也有不少的解决方案:
方案 | 适用场景 | 优势 | 限制 |
---|---|---|---|
CORS | API 服务器 | 简单灵活 | 需要服务器支持 |
JSONP | 仅 GET 请求 | 兼容性好 | 只能 GET,有安全风险 |
反向代理 | 生产环境 | 完全绕过跨域 | 需要 Nginx 或其他代理 |
postMessage | iframe 通信 | 适用于嵌套页面 | 需要双方配合 |