Interview AiBox logo

Interview AiBox 实时 AI 助手,让你自信应答每一场面试

download免费下载
3local_fire_department59 次面试更新于 2025-08-23account_tree思维导图

什么是跨域?有哪些解决跨域的方法?

lightbulb

题型摘要

跨域是Web开发中因浏览器同源策略导致的限制,当协议、域名或端口不同时发生。解决跨域的主要方法有:1) CORS(跨域资源共享),通过服务器设置HTTP响应头实现,是最推荐的标准化方案;2) JSONP,利用script标签跨域特性,但仅支持GET请求;3) 代理服务器,通过同源服务器转发请求;4) WebSocket,双向通信协议,不受同源限制;5) postMessage,HTML5 API,用于窗口间安全通信;6) document.domain,适用于子域间通信;7) window.name和location.hash,利用浏览器特性实现但安全性较低。选择方案需考虑安全性、兼容性、通信类型和实现复杂度等因素。

什么是跨域?有哪些解决跨域的方法?

跨域的定义

跨域(Cross-Origin)指的是在Web开发中,一个域下的文档或脚本试图去请求另一个域下的资源时,由于浏览器的同源策略(Same-Origin Policy)而产生的限制。

同源策略

同源策略是浏览器的一种安全机制,它阻止一个域的JavaScript脚本与另一个域的资源进行交互。所谓同源是指:

  • 协议相同(如http、https)
  • 域名相同(如example.com)
  • 端口相同(如80、443)

只要这三者中有一个不同,就构成了跨域。

--- title: 同源与跨域对比 --- graph TD A[同源] --> B[协议相同] A --> C[域名相同] A --> D[端口相同] E[跨域] --> F[协议不同] E --> G[域名不同] E --> H[端口不同] I[示例] --> J[http://example.com/page1.html] I --> K[http://example.com/page2.html] I --> L[同源] M[示例] --> N[http://example.com/page1.html] M --> O[https://example.com/page2.html] M --> P[跨域:协议不同]

跨域的场景

跨域通常发生在以下场景:

  1. AJAX请求:使用XMLHttpRequest或Fetch API请求不同源的资源
  2. Cookie/LocalStorage访问:尝试访问不同源的Cookie或LocalStorage
  3. DOM访问:尝试通过JavaScript访问不同源页面的DOM元素
  4. 资源引用:如引用不同源的图片、CSS、脚本等(这些通常被允许,但有一些限制)

解决跨域的方法

1. CORS(跨域资源共享)

CORS(Cross-Origin Resource Sharing)是W3C标准,是最常用的跨域解决方案。它通过服务器设置HTTP响应头,告知浏览器哪些域可以访问资源。

实现方式

服务器端设置响应头:

// Node.js Express示例
app.use((req, res, next) => {
  // 允许所有来源
  res.header('Access-Control-Allow-Origin', '*');
  // 允许的请求方法
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
  // 允许的请求头
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  // 允许发送Cookie
  res.header('Access-Control-Allow-Credentials', 'true');
  // 预检请求的缓存时间
  res.header('Access-Control-Max-Age', '86400');
  next();
});

客户端发送请求:

// 普通请求
fetch('http://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data));

// 带Cookie的请求
fetch('http://api.example.com/data', {
  credentials: 'include'
});

优点

  • 标准化解决方案,支持所有HTTP请求方法
  • 服务器端控制,安全性高
  • 支持Cookie和HTTP认证

缺点

  • 需要服务器端配合
  • 对于旧版浏览器支持有限

2. JSONP(JSON with Padding)

JSONP是一种非官方的跨域解决方案,利用了<script>标签的跨域特性。

实现方式

客户端:

// 创建script标签
function jsonp(url, callback) {
  const script = document.createElement('script');
  const callbackName = 'jsonp_callback_' + Date.now();
  
  // 将回调函数挂载到window对象上
  window[callbackName] = function(data) {
    delete window[callbackName];
    document.body.removeChild(script);
    callback(data);
  };
  
  // 构建请求URL
  script.src = `${url}?callback=${callbackName}`;
  document.body.appendChild(script);
}

// 使用示例
jsonp('http://api.example.com/data', function(data) {
  console.log(data);
});

服务器端:

// Node.js示例
app.get('/data', (req, res) => {
  const callback = req.query.callback;
  const data = { name: 'example', value: 123 };
  
  // 返回包裹在回调函数中的JSON数据
  res.send(`${callback}(${JSON.stringify(data)})`);
});

优点

  • 兼容性好,支持旧版浏览器
  • 实现简单

缺点

  • 只支持GET请求
  • 安全性较低,容易受到XSS攻击
  • 服务器端需要特殊处理
  • 错误处理困难

3. 代理服务器

通过同源服务器作为代理,将跨域请求转发到目标服务器。

实现方式

开发环境代理(以webpack-dev-server为例):

// webpack.config.js
module.exports = {
  // ...其他配置
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        changeOrigin: true,
        pathRewrite: {
          '^/api': ''
        }
      }
    }
  }
};

生产环境代理(以Nginx为例):

server {
    listen 80;
    server_name yourdomain.com;
    
    location /api/ {
        proxy_pass http://api.example.com/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

优点

  • 对前端透明,无需修改前端代码
  • 可以隐藏后端服务真实地址,增加安全性
  • 可以统一处理请求头、Cookie等

缺点

  • 需要额外的服务器资源
  • 增加了请求的延迟
  • 需要服务器配置权限

4. WebSocket

WebSocket是一种双向通信协议,它不受同源策略限制,可以用于跨域通信。

实现方式

客户端:

// 创建WebSocket连接
const socket = new WebSocket('ws://api.example.com/ws');

// 连接打开
socket.onopen = function(event) {
  console.log('连接已建立');
  // 发送消息
  socket.send(JSON.stringify({ type: 'request', data: 'hello' }));
};

// 接收消息
socket.onmessage = function(event) {
  const data = JSON.parse(event.data);
  console.log('收到消息:', data);
};

// 连接关闭
socket.onclose = function(event) {
  console.log('连接已关闭');
};

// 错误处理
socket.onerror = function(error) {
  console.error('WebSocket错误:', error);
};

服务器端(Node.js示例):

const WebSocket = require('ws');

// 创建WebSocket服务器
const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {
  console.log('客户端已连接');
  
  // 接收消息
  ws.on('message', function incoming(message) {
    console.log('收到消息:', message);
    
    // 发送消息
    ws.send(JSON.stringify({ type: 'response', data: 'hello back' }));
  });
  
  // 连接关闭
  ws.on('close', function() {
    console.log('客户端已断开连接');
  });
});

优点

  • 支持双向实时通信
  • 不受同源策略限制
  • 通信效率高

缺点

  • 需要专门的WebSocket服务器
  • 协议与HTTP不同,需要特殊处理
  • 连接状态管理复杂

5. postMessage

postMessage是HTML5引入的API,允许不同窗口(包括iframe)之间进行安全通信。

实现方式

发送消息:

// 父窗口向iframe发送消息
const iframe = document.getElementById('my-iframe').contentWindow;
iframe.postMessage('Hello from parent', 'http://iframe-domain.com');

// 或者新窗口
const newWindow = window.open('http://example.com');
newWindow.postMessage('Hello from opener', 'http://example.com');

接收消息:

// 在iframe或新窗口中接收消息
window.addEventListener('message', function(event) {
  // 验证消息来源
  if (event.origin !== 'http://parent-domain.com') {
    return;
  }
  
  console.log('收到消息:', event.data);
  
  // 回复消息
  event.source.postMessage('Hello from iframe', event.origin);
});

优点

  • 安全性高,可以指定消息来源
  • 支持不同窗口间的通信
  • 可以传递各种类型的数据

缺点

  • 需要双方都实现消息监听
  • 只适用于窗口间通信,不适用于普通AJAX请求

6. document.domain

document.domain方法适用于子域之间的跨域通信,通过设置相同的domain值来实现。

实现方式

<!-- 在父页面 http://parent.example.com 中 -->
<script>
  document.domain = 'example.com';
  
  // 访问iframe
  const iframe = document.getElementById('my-iframe');
  iframe.onload = function() {
    // 现在可以访问iframe的document
    const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
    console.log(iframeDoc.body.innerHTML);
  };
</script>

<!-- 在iframe页面 http://child.example.com 中 -->
<script>
  document.domain = 'example.com';
</script>

优点

  • 实现简单
  • 适用于子域之间的通信

缺点

  • 只适用于子域之间
  • 安全性较低,容易受到攻击
  • 现代浏览器对此有限制

7. window.name

window.name方法利用window对象在不同页面加载时保持name值的特性来实现跨域通信。

实现方式

<!-- 在页面A中 -->
<script>
  // 创建一个隐藏的iframe
  const iframe = document.createElement('iframe');
  iframe.style.display = 'none';
  
  // 设置iframe的src为跨域页面
  iframe.src = 'http://other-domain.com/data.html';
  
  // iframe加载完成后,读取window.name
  iframe.onload = function() {
    // 读取数据
    const data = iframe.contentWindow.name;
    console.log('跨域数据:', data);
    
    // 清理
    iframe.onload = null;
    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
  };
  
  document.body.appendChild(iframe);
</script>

<!-- 在跨域页面 http://other-domain.com/data.html 中 -->
<script>
  // 将数据存储在window.name中
  window.name = JSON.stringify({ key: 'value', data: [1, 2, 3] });
</script>

优点

  • 可以传递较大数据(window.name支持约2MB数据)
  • 实现相对简单

缺点

  • 数据暴露在window.name中,安全性较低
  • 只适用于单向通信
  • 需要借助iframe等元素

8. location.hash

location.hash方法利用URL的hash部分(#后面的内容)在不同域之间传递数据。

实现方式

<!-- 在父页面 http://parent.com 中 -->
<iframe id="myFrame" src="http://child.com/#data" style="display:none;"></iframe>

<script>
  // 监听hash变化
  window.addEventListener('hashchange', function() {
    const data = location.hash.substring(1);
    console.log('收到数据:', data);
  });
  
  // 向iframe发送数据
  function sendDataToChild(data) {
    const iframe = document.getElementById('myFrame');
    iframe.src = 'http://child.com/#' + encodeURIComponent(data);
  }
</script>

<!-- 在iframe页面 http://child.com 中 -->
<script>
  // 获取父页面传递的数据
  const data = decodeURIComponent(location.hash.substring(1));
  console.log('收到数据:', data);
  
  // 向父页面发送数据
  function sendDataToParent(data) {
    parent.location.href = 'http://parent.com/#' + encodeURIComponent(data);
  }
</script>

优点

  • 实现简单
  • 不需要服务器支持

缺点

  • 数据直接暴露在URL中,安全性低
  • 数据大小有限(URL长度限制)
  • 会有历史记录,可能影响用户体验

跨域解决方案对比

--- title: 跨域解决方案对比 --- graph TD subgraph 跨域解决方案 A[CORS] --> A1[标准化解决方案] A --> A2[服务器端控制] A --> A3[支持所有HTTP方法] B[JSONP] --> B1[只支持GET请求] B --> B2[兼容性好] B --> B3[安全性较低] C[代理服务器] --> C1[对前端透明] C --> C2[需要额外服务器资源] C --> C3[可隐藏后端服务] D[WebSocket] --> D1[双向通信] D --> D2[实时性好] D --> D3[需要专门服务器] E[postMessage] --> E1[窗口间通信] E --> E2[安全性高] E --> E3[需要双方实现] F[document.domain] --> F1[仅适用于子域] F --> F2[实现简单] F --> F3[安全性较低] G[window.name] --> G1[可传递较大数据] G --> G2[安全性较低] G --> G3[需要借助iframe] H[location.hash] --> H1[实现简单] H --> H2[数据暴露在URL] H --> H3[有历史记录] end

如何选择合适的跨域解决方案

选择合适的跨域解决方案需要考虑以下因素:

  1. 安全性要求:对于敏感数据,应优先选择CORS或postMessage等安全性较高的方案
  2. 兼容性需求:如果需要支持旧版浏览器,可以考虑JSONP
  3. 通信类型:双向实时通信选择WebSocket,单向请求选择CORS或代理
  4. 开发环境:开发阶段可以使用代理服务器,生产环境可以使用CORS
  5. 性能要求:对于高频请求,WebSocket性能更好
  6. 实现复杂度:简单场景可以使用JSONP或location.hash,复杂场景建议使用CORS

总结

跨域是Web开发中的常见问题,由浏览器的同源策略引起。解决跨域的方法有多种,包括CORS、JSONP、代理服务器、WebSocket、postMessage等。每种方法都有其适用场景和优缺点,开发者需要根据具体需求选择合适的解决方案。在现代Web开发中,CORS是最常用和推荐的跨域解决方案,它提供了标准化的方式来安全地处理跨域请求。

account_tree

思维导图

Interview AiBox logo

Interview AiBox — 面试搭档

不只是准备,更是实时陪练

Interview AiBox 在面试过程中提供实时屏幕提示、AI 模拟面试和智能复盘,让你每一次回答都更有信心。

AI 助读

一键发送到常用 AI

跨域是Web开发中因浏览器同源策略导致的限制,当协议、域名或端口不同时发生。解决跨域的主要方法有:1) CORS(跨域资源共享),通过服务器设置HTTP响应头实现,是最推荐的标准化方案;2) JSONP,利用script标签跨域特性,但仅支持GET请求;3) 代理服务器,通过同源服务器转发请求;4) WebSocket,双向通信协议,不受同源限制;5) postMessage,HTML5 API,用于窗口间安全通信;6) document.domain,适用于子域间通信;7) window.name和location.hash,利用浏览器特性实现但安全性较低。选择方案需考虑安全性、兼容性、通信类型和实现复杂度等因素。

智能总结

深度解读

考点定位

思路启发

auto_awesome

相关题目

请做一个自我介绍

自我介绍是面试的开场环节,应遵循"三段式"结构:基本信息与教育背景、核心能力与项目经验、求职动机与个人特质。重点突出与岗位相关的技能和经验,用具体数据和成果支撑,保持真诚自然的表达,控制在2-3分钟内。针对不同公司和岗位进行个性化调整,展示自己的匹配度和价值。

arrow_forward

你有什么问题想问我们公司或团队的吗?

面试结尾提问是展示面试者思考深度和职业素养的重要机会。应提前准备3-5个有深度的问题,围绕团队技术、个人成长、公司文化和业务发展四个方面。好的问题能体现你对公司的了解、对职位的重视以及你的职业规划,避免问基础信息类问题。

arrow_forward

请做一个自我介绍

自我介绍应遵循“我是谁-我为什么能胜任-我为什么想来”的逻辑框架。在“能胜任”部分,要通过STAR法则和量化结果来突出技术亮点和项目经验。在“想来”部分,要表达对华为技术、文化或业务的认同,展现匹配度和诚意。整个过程应简洁有力,控制在1-3分钟内。

arrow_forward

请做一个自我介绍

自我介绍是面试的开场环节,应简洁明了地展示个人基本信息、教育背景、项目经验、技术特长、个人特质和求职动机。优秀的自我介绍应结构清晰、重点突出,与应聘岗位高度匹配,并表达出对公司的了解和加入的强烈意愿。

arrow_forward

请做一个自我介绍,包括你的技术背景、项目经验和学习方向。

自我介绍应包含四个核心部分:个人背景、技术能力、项目经验和学习规划。技术背景需突出前端技术栈掌握程度;项目经验应选择代表性案例,说明技术实现和个人贡献;学习方向要体现职业规划与公司发展的契合度。整体表达应简洁有力,重点突出,时间控制在3-5分钟内。

arrow_forward

阅读状态

阅读时长

9 分钟

阅读进度

7%

章节:15 · 已读:1

当前章节: 跨域的定义

最近更新:2025-08-23

本页目录

Interview AiBox logo

Interview AiBox

AI 面试实时助手

面试中屏幕实时显示参考回答,帮你打磨表达。

免费下载download

分享题目

复制链接,或一键分享到常用平台

外部分享