大家好,我是有一点想法的thinkmars,目前在准备面试与工作,借着间隙时间学习复习,写一点基础文章,欢迎想找工作的人与我一起学习,一起讨饭吃~
前言
前后端分离与浏览器安全限制使得跨域成为前端开发绕不过的问题。目前跨域对开发最大的影响是本地开发请求服务器资源,生产环境多数已经通过CORS配置好。跨域是前后端对接数据的第一道坎,也是比较能考验web开发合作经验的一个问题。
什么是跨域(Cross-Origin)?
跨域(Cross-Origin)是指浏览器出于安全考虑,限制了来自不同 源(Origin) 的 JavaScript 代码访问资源的权限。
1. 什么是"同源"?
浏览器的 同源策略(Same-Origin Policy, SOP) 规定,只有当两个 URL 的 协议(Protocol)、域名(Domain)、端口(Port) 完全一致时,才属于同源,否则就是跨域。
URL A | URL B | 是否同源 | 原因 |
---|---|---|---|
https://example.com |
https://example.com |
✅ 同源 | 协议、域名、端口相同 |
https://example.com |
http://example.com |
❌ 跨域 | 协议不同(HTTPS vs HTTP) |
https://example.com |
https://api.example.com |
❌ 跨域 | 子域名不同 |
https://example.com:80 |
https://example.com:443 |
❌ 跨域 | 端口不同 |
2. 跨域的限制范围
跨域限制主要影响以下操作:
- AJAX / Fetch 请求(XMLHttpRequest、axios、fetch API)
- Web 字体(@font-face)
- Canvas 绘制跨域图片
- Web Storage / IndexedDB(部分浏览器限制)
但以下情况 不受跨域限制:
<img>
、<script>
、<link>
、<iframe>
等标签的src
或href
属性(但 JavaScript 无法直接读取返回内容)。- WebSocket(不受 SOP 限制,但可能受 CORS 影响)!!!!!!。
3. 为什么要有跨域限制?
- 安全考虑:防止恶意网站窃取用户数据(如 Cookie、LocalStorage)。
- 防止 CSRF(跨站请求伪造):避免攻击者诱导用户发送恶意请求。
4. 常见的跨域场景
- 前端运行在
http://localhost:3000
,但请求后端 APIhttps://api.example.com
。 - 主站
https://www.example.com
请求子域https://api.example.com
。 - 使用 CDN 资源时,不同域名导致跨域(如
https://cdn.example.com
)。
前端解决跨域解决方案
1. CORS(跨域资源共享)最常用
-
原理 :服务器设置响应头(如
Access-Control-Allow-Origin
)允许特定域访问资源。 -
适用场景:前后端分离项目,后端可控。
-
示例 :
httpAccess-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST
2. JSONP(JSON with Padding)
-
原理 :利用
<script>
标签不受同源策略限制的特性,通过动态创建脚本获取数据。 -
缺点:仅支持 GET 请求,安全性较低。
-
示例 :
javascriptfunction handleResponse(data) { console.log(data); } const script = document.createElement('script'); script.src = 'https://api.example.com/data?callback=handleResponse'; document.body.appendChild(script);
3. 代理服务器(Proxy)
- 原理:前端请求同域代理服务器,由代理服务器转发请求到目标服务器。
- 实现方式 :
-
开发环境 :使用 webpack-dev-server、Vite 或 http-proxy-middleware。
javascript// webpack.config.js devServer: { // 代理所有以 /api 开头的请求 '/api': { target: 'http://your-backend-server.com', // 目标服务器地址 changeOrigin: true, // 改变请求头中的host为目标URL pathRewrite: { '^/api': '' // 重写路径,去掉/api前缀 }, secure: false, // 如果是https接口,需要配置这个参数 // 其他可选配置 // headers: { // 'X-Custom-Header': 'foobar' // } } } // vite.config.js server: { proxy: { // 字符串简写写法 '/foo': 'http://localhost:4567', // 完整写法 '/api': { target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, ws: true, // 是否代理 WebSocket(默认为 true) rewrite: (path) => path.replace(/^/api/, ''), // 更多配置 // configure: (proxy, options) => { // // proxy 是 'http-proxy' 的实例 // } }, // 正则表达式写法 '^/fallback/.*': { target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, rewrite: (path) => path.replace(/^/fallback/, '') } } }
-
生产环境 :通过 Nginx 反向代理。
nginxlocation /api { proxy_pass https://api.example.com; }
-
4. WebSocket
-
原理:WebSocket 协议不受同源策略限制,适用于实时通信。
-
示例 :
javascriptconst socket = new WebSocket('wss://api.example.com'); socket.onmessage = (event) => { console.log(event.data); };
5. postMessage
-
原理 :通过
window.postMessage
实现不同窗口(如 iframe、弹窗)间的跨域通信。 -
示例 :
javascript// 发送方 window.parent.postMessage('Hello', 'https://target.com'); // 接收方 window.addEventListener('message', (event) => { if (event.origin === 'https://source.com') { console.log(event.data); } });
6. 修改 document.domain
-
原理 :将子域和父域的
document.domain
设置为相同值(仅适用于主域相同的情况)。 -
示例 :
javascript// a.example.com 和 b.example.com document.domain = 'example.com';
7. 跨域资源共享的其他头部
- 服务器可设置更多 CORS 头部以细化控制:
Access-Control-Allow-Headers
: 允许的自定义头。Access-Control-Allow-Credentials
: 是否允许携带 Cookie。
8. 浏览器扩展或插件
-
临时解决方案:如 Chrome 的跨域插件(
Allow CORS
),或启动浏览器时禁用安全策略(仅开发用):bashchrome.exe --disable-web-security --user-data-dir=/tmp
9. 服务端转发(BFF模式)
- 后端提供一个统一接口聚合第三方服务,前端只与同域后端交互。
注意事项:
- 安全性:确保 CORS 或代理配置不会开放敏感资源。
- 生产环境:优先使用 CORS 或代理,避免 JSONP 等不安全方案。
根据项目需求(如开发环境调试、生产部署、实时通信等)选择合适的方案。