跨域(Cross-Origin)是指在浏览器中执行的 Web 请求时,域名、协议或端口号不同时,浏览器由于安全性原因会限制此类请求的问题,源于浏览器的同源策略。为了解决跨域问题,开发者可以选择多种方案,如 JSONP、CORS、代理服务器、iframe
+postMessage
、WebSocket 等。
同源策略
同源策略是浏览器的一种重要安全机制,用于防止恶意网站通过 JavaScript 访问另一个网站的敏感数据。浏览器将两个 URL 视为同源(Same-Origin),只有在它们的协议(如 http
或 https
)、域名(如 example.com
)、和端口号(如 80
或 443
)完全相同时,才允许它们之间进行资源共享。
- 同源的定义:
- 协议相同
- 域名相同
- 端口相同
跨域问题产生的场景
跨域问题主要在以下几种情况下产生:
- AJAX 请求: 当使用 XMLHttpRequest 或 Fetch API 进行跨域请求时,会因为同源策略而被浏览器阻止。
- 跨域资源嵌入: 如
<script>
、<img>
、<iframe>
标签加载来自不同域的资源,这些标签的默认行为是允许跨域的,但涉及到 JavaScript 的交互可能会被阻止。 - 跨域字体加载: 使用 CSS
@font-face
加载来自不同域的字体文件。 - 跨域样式表和脚本: CSS 样式表的加载和跨域的 JavaScript 文件可能会导致样式隔离或脚本无法执行。
解决跨域问题的常用方法
1. JSONP (JSON with Padding)
JSONP 是一种早期的跨域解决方案,主要用于跨域获取数据。
原理: 利用
<script>
标签不受同源策略限制的特点,将请求的数据作为 JavaScript 文件加载,并在服务器端将返回的数据包装成一个指定的回调函数。例如:
javascript
<script src="http://example.com/data?callback=myCallback"></script> function myCallback(data) { console.log(data); }
缺点: JSONP 只支持
GET
请求,且存在安全隐患,因为返回的内容是执行的 JavaScript 代码,可能会导致 XSS 攻击。
2. CORS (Cross-Origin Resource Sharing)
CORS 是现代浏览器支持的跨域资源共享机制,它允许服务器声明哪些来源可以访问它的资源。
原理: 通过设置 HTTP 头来控制跨域请求。
- Access-Control-Allow-Origin: 指定允许访问资源的域。
- Access-Control-Allow-Methods: 指定允许的请求方法,如
GET
,POST
,PUT
,DELETE
。 - Access-Control-Allow-Headers: 指定允许的请求头。
- Access-Control-Allow-Credentials: 指定是否允许发送 Cookie。
简单请求: 如
GET
或POST
请求时,浏览器会自动处理 CORS 头,直接进行请求。预检请求 (Preflight Request): 对于复杂请求(如
PUT
,DELETE
或带自定义头的请求),浏览器会在发送正式请求前,发送一个OPTIONS
请求,称为“预检请求”。服务器响应该请求后,浏览器才会发出实际的请求。示例:
httpAccess-Control-Allow-Origin: https://example.com Access-Control-Allow-Methods: GET, POST Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Credentials: true
3. 代理服务器
通过配置代理服务器,可以将跨域请求转发到同源服务器上,然后由代理服务器代为发送请求。
原理: 前端的请求实际上发送到同源的代理服务器,代理服务器再将请求转发到目标服务器。因为前端与代理服务器是同源的,所以不会触发跨域问题。
应用场景: 可以在开发环境中通过配置 Webpack 的 DevServer 代理,或在生产环境中通过配置 Nginx 反向代理。
示例:
javascript
// Webpack DevServer 配置 devServer: { proxy: { '/api': { target: 'http://api.example.com', changeOrigin: true, pathRewrite: { '^/api': '' } } } }
4. 服务器端通过 iframe 与 postMessage 进行跨域通信
原理: 通过嵌套
iframe
和postMessage
方法,可以实现跨域数据通信。postMessage
是 HTML5 提供的 API,用于安全地实现不同源的窗口间通信。示例:
javascript
// 在子页面中 window.parent.postMessage('Hello from iframe', 'http://parent.com'); // 在父页面中 window.addEventListener('message', function(event) { if (event.origin === 'http://child.com') { console.log(event.data); // Hello from iframe } }, false);
5. WebSocket
WebSocket 是一种支持双向通信的协议,它在建立连接时只受同源策略的限制,但连接建立后,通信不再受同源策略影响。
- 应用场景: WebSocket 通常用于实时通信,如聊天应用、在线游戏等。
- 跨域特点: WebSocket 的握手是通过 HTTP 请求完成的,浏览器只会在连接建立时检查同源策略,连接建立后可以跨域发送和接收消息。
常见的跨域请求类型
- 跨域 Ajax 请求: 通过 XMLHttpRequest 或 Fetch API 发起的跨域请求,需要使用 CORS。
- 跨域资源加载: 通过
<script>
、<link>
、<img>
、<iframe>
等标签加载的资源,通常不受同源策略的限制,但 JavaScript 的跨域交互(如使用window.postMessage
)仍然需要注意。 - 跨域字体加载: 通过 CSS
@font-face
加载跨域字体时,需要服务器允许跨域请求(通过设置 CORS 头)。