大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。
我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
引言:一个让人抓狂的前端问题
最近我在做一个项目,需要从 https://www.my-site.com 向 https://api.other-site.com 发送请求获取数据。结果,浏览器无情地拒绝了我的请求,并抛出一个错误:
Access to fetch at 'https://api.other-site.com/data' from origin 'https://www.my-site.com' has been blocked by CORS policy...
相信不少前端开发者都遇到过类似的报错。为什么浏览器要阻止我的请求? 这背后就是 同源策略(Same-Origin Policy, SOP) 在起作用。今天,我们就来深入聊聊这个机制,理解它的原理,并学会如何合理绕过它。
1. 什么是同源策略?
同源策略是浏览器最核心的安全机制之一,它限制了 不同源的网页脚本 如何互相访问数据。简单来说,浏览器只允许来自相同源的脚本自由交互,而不同源的交互会受到严格限制。
什么是"同源"?
两个 URL 如果满足以下三个条件,就是同源的:
- 协议(Protocol) 相同(
http/https) - 域名(Domain) 相同(
www.example.com) - 端口(Port) 相同(默认
80或443)
示例:判断是否同源
| URL 1 | URL 2 | 是否同源 | 原因 |
|---|---|---|---|
https://example.com | https://example.com/api | ✅ 是 | 协议、域名、端口相同 |
http://example.com | https://example.com | ❌ 否 | 协议不同(HTTP vs HTTPS) |
https://example.com | https://api.example.com | ❌ 否 | 域名不同(主域 vs 子域) |
https://example.com:8080 | https://example.com | ❌ 否 | 端口不同(8080 vs 443) |
2. 同源策略限制了哪些操作?
同源策略主要限制以下几种行为:
-
AJAX / Fetch 跨域请求
- 默认情况下,
XMLHttpRequest和fetch()不能访问不同源的资源。 - 除非目标服务器明确允许(通过 CORS)。
- 默认情况下,
-
DOM 访问限制
- 不同源的
iframe不能直接访问其contentDocument(除非设置document.domain或使用postMessage)。
- 不同源的
-
Cookie / LocalStorage / IndexedDB 隔离
- 不同源的网站无法互相读取对方的存储数据。
3. 为什么需要同源策略?
假设没有同源策略,会发生什么?
- 恶意网站可以窃取你的数据
比如,你登录了https://your-bank.com,然后访问了一个恶意网站https://evil-site.com,它可以通过 AJAX 偷偷访问你的银行数据。 - CSRF(跨站请求伪造)攻击更容易发生
攻击者可以在你不知情的情况下,利用你的登录态向银行发起转账请求。
同源策略就像是浏览器的一道安全门,防止恶意网站随意访问你的数据。
4. 如何绕过同源策略?(合法方式)
虽然同源策略很重要,但在实际开发中,我们经常需要跨域访问资源。以下是几种常见的解决方案:
(1)CORS(跨域资源共享)
CORS 是官方推荐的跨域方案,需要后端配合。服务器在响应头中添加 Access-Control-Allow-Origin 来允许特定源的请求。
示例(后端设置):
Access-Control-Allow-Origin: https://www.my-site.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type
前端直接使用 fetch:
const response = await fetch('https://api.other-site.com/data');
const data = await response.json();
(2)JSONP(仅限 GET 请求)
JSONP 利用 <script> 标签不受同源策略限制的特性,适用于老式 API。
前端代码:
function handleResponse(data) {
console.log(data);
}
const script = document.createElement('script');
script.src = 'https://api.other-site.com/data?callback=handleResponse';
document.body.appendChild(script);
后端返回:
handleResponse({ "name": "我", "age": 25 });
(3)代理服务器
如果你的后端不支持 CORS,可以自己搭建一个代理服务器:
// Node.js 代理示例(Express)
app.get('/proxy/data', async (req, res) => {
const response = await fetch('https://api.other-site.com/data');
const data = await response.json();
res.json(data);
});
前端调用:
const response = await fetch('/proxy/data');
const data = await response.json();
(4)document.domain(仅适用于相同主域)
如果两个页面属于同一主域(如 a.example.com 和 b.example.com),可以设置:
document.domain = 'example.com';
这样它们就可以互相访问 DOM。
5. 实际开发中的坑与解决方案
(1)开发环境跨域问题
在本地开发时,前端运行在 http://localhost:3000,但 API 在 https://api.example.com,如何解决?
方案 1:配置 Webpack DevServer 代理
// vue.config.js / webpack.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'https://api.example.com',
changeOrigin: true,
}
}
}
};
方案 2:浏览器禁用安全策略(仅限测试)
Chrome 启动时加参数:
chrome.exe --disable-web-security --user-data-dir="C:/Temp"
(2)withCredentials 携带 Cookie
如果跨域请求需要携带 Cookie,必须设置:
fetch('https://api.example.com/data', {
credentials: 'include'
});
同时,后端必须设置:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://www.my-site.com // 不能是 *
6. 总结
同源策略是浏览器安全的核心机制,但它也给前端开发带来了一些挑战。我们可以通过:
- CORS(推荐)
- JSONP(老项目兼容)
- 代理服务器(灵活可控)
document.domain(同主域适用)
来合法绕过限制。理解这些机制,能让你在前端开发中更游刃有余地处理跨域问题。
你在项目中遇到过哪些跨域问题?欢迎在评论区分享你的经历! 🚀