跨域问题产生的原因及解决方法
1. 什么是跨域?
跨域是指浏览器从一个源(Origin)的文档或脚本,去请求另一个源的资源时,就会触发跨域。
** 源(Origin)** 由三部分组成:
- 协议(Protocol)
- 域名(Host)
- 端口(Port)
只要这三者中有一个不同,就属于跨域。
例子:
2. 为什么会有跨域限制(同源策略)?
跨域限制是浏览器的 ** 同源策略(Same-Origin Policy, SOP)** 的安全机制,主要是为了防止:
- 恶意网站读取另一个网站的 Cookie
- 防止 CSRF(跨站请求伪造)攻击
- 保护用户敏感数据
简单来说:浏览器不允许 JavaScript 向不同源的服务器发送请求并获取响应,除非服务器明确允许。
3. 跨域请求的三种常见场景
- AJAX / Fetch 请求(最常见)
- DOM 访问(iframe 之间)
- 本地存储访问(localStorage / IndexedDB)
4. 跨域问题的解决方法
4.1 CORS(跨源资源共享)
原理:在服务器响应头中添加 Access-Control-Allow-Origin
等字段,明确允许哪些源访问资源。
简单请求条件:
- 请求方法是 GET、HEAD、POST
- 请求头只包含简单字段(Accept、Accept-Language、Content-Type 等)
- Content-Type 为 application/x-www-form-urlencoded、multipart/form-data 或 text/plain
代码示例:
前端(index.html):
<script>
fetch('http://api.example.com/data').then(res => res.json()).then(data => console.log(data)).catch(err => console.error(err));
</script>
后端(Node.js + Express):
const express = require('express');
const app = express();// 允许所有源访问
app.use((req, res, next) => {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');next();
});app.get('/data', (req, res) => {res.json({ message: 'Hello from API' });
});app.listen(3000, () => {console.log('API server running at http://localhost:3000');
});
4.2 代理服务器(Proxy)
原理:同源策略只存在于浏览器和服务器之间,服务器和服务器之间没有跨域限制。因此可以在本地启动一个代理服务器,让它去请求目标服务器,再把结果返回给浏览器。
前端(Vue CLI 配置 proxy)
// vue.config.js
module.exports = {devServer: {proxy: {'/api': {target: 'http://api.example.com',changeOrigin: true,pathRewrite: {'^/api': ''}}}}
};
请求时:
fetch('/api/data').then(res => res.json()).then(data => console.log(data));
4.3 JSONP(JSON with Padding)
原理:利用 <script>
标签没有跨域限制的特性,通过动态创建 <script>
标签来请求数据,服务器返回一个函数调用。
注意:只支持 GET 请求。
前端:
<script>
function handleResponse(data) {console.log('JSONP response:', data);
}const script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);
</script>
后端(Node.js):
app.get('/data', (req, res) => {const callback = req.query.callback;const data = JSON.stringify({ message: 'Hello JSONP' });res.send(`${callback}(${data})`);
});
4.4 document.domain
原理:当两个页面属于同一个主域的不同子域时,可以通过设置 document.domain
让它们同域。
页面 A(a.example.com)
<script>
document.domain = 'example.com';
</script>
页面 B(b.example.com)
<script>
document.domain = 'example.com';
</script>
这样两个页面就可以互相访问对方的 DOM。
4.5 postMessage
原理:HTML5 提供的 postMessage
方法可以在不同源的页面之间安全地传递数据。
父页面:
<iframe id="iframe" src="http://other-domain.com"></iframe>
<script>
const iframe = document.getElementById('iframe');
iframe.onload = () => {iframe.contentWindow.postMessage('Hello from parent', 'http://other-domain.com');
};window.addEventListener('message', (e) => {if (e.origin === 'http://other-domain.com') {console.log('Received from iframe:', e.data);}
});
</script>
子页面(other-domain.com):
<script>
window.addEventListener('message', (e) => {if (e.origin === 'http://parent-domain.com') {console.log('Received from parent:', e.data);e.source.postMessage('Hello from iframe', e.origin);}
});
</script>
5. 各种方法对比
6. 总结
- 跨域是浏览器同源策略的安全限制
- 解决跨域的主要方法有:
- CORS(生产环境推荐)
- 代理服务器(开发环境常用)
- JSONP(兼容旧浏览器)
- document.domain(同主域子域通信)
- postMessage(跨域页面通信)
7.跨域解决方案对比流程图
流程图特点
-
按场景分类:
- AJAX/Fetch 请求
- iframe 通信
- 本地存储共享
- WebSocket 通信
-
根据条件选择方案:
- 是否能修改服务器
- 是否需要兼容旧浏览器
- 是否同主域的子域
-
突出推荐方案:
- 生产环境首选 CORS
- 开发环境常用代理服务器
- 跨域页面通信首选 postMessage
使用建议:
- 如果是现代 Web 应用,推荐用 CORS 解决跨域
- 如果是开发调试阶段,用 代理服务器 最简单
- 如果是跨域页面通信,用 postMessage
- 如果必须兼容老旧浏览器,才考虑 JSONP