Skip to content

跨域(Cross-Origin)是指在浏览器中执行的 Web 请求时,域名、协议或端口号不同时,浏览器由于安全性原因会限制此类请求的问题,源于浏览器的同源策略。为了解决跨域问题,开发者可以选择多种方案,如 JSONP、CORS、代理服务器、iframe+postMessage、WebSocket 等。

同源策略

同源策略是浏览器的一种重要安全机制,用于防止恶意网站通过 JavaScript 访问另一个网站的敏感数据。浏览器将两个 URL 视为同源(Same-Origin),只有在它们的协议(如 httphttps)、域名(如 example.com)、和端口号(如 80443)完全相同时,才允许它们之间进行资源共享。

  • 同源的定义:
    • 协议相同
    • 域名相同
    • 端口相同

跨域问题产生的场景

跨域问题主要在以下几种情况下产生:

  • 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。
  • 简单请求: 如 GETPOST 请求时,浏览器会自动处理 CORS 头,直接进行请求。

  • 预检请求 (Preflight Request): 对于复杂请求(如 PUT, DELETE 或带自定义头的请求),浏览器会在发送正式请求前,发送一个 OPTIONS 请求,称为“预检请求”。服务器响应该请求后,浏览器才会发出实际的请求。

  • 示例:

    http
    Access-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 进行跨域通信

  • 原理: 通过嵌套 iframepostMessage 方法,可以实现跨域数据通信。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 头)。