阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 跨域请求处理

跨域请求处理

作者:陈川 阅读数:20477人阅读 分类: JavaScript

跨域请求的基本概念

跨域请求是指浏览器出于安全考虑,限制从一个源加载的脚本与另一个源的资源进行交互。同源策略要求协议、域名和端口完全相同才算同源。当这三个要素中任意一个不同时,就会产生跨域问题。例如:

  • http://a.com 请求 https://a.com(协议不同)
  • http://a.com 请求 http://b.com(域名不同)
  • http://a.com:80 请求 http://a.com:8080(端口不同)

常见的跨域解决方案

JSONP

JSONP利用<script>标签不受同源策略限制的特性实现跨域请求。它通过动态创建script标签,将回调函数名作为参数传递给服务器,服务器返回数据时调用该函数。

function handleResponse(data) {
  console.log('Received data:', data);
}

const script = document.createElement('script');
script.src = 'http://example.com/api?callback=handleResponse';
document.body.appendChild(script);

CORS(跨域资源共享)

CORS是现代浏览器支持的标准跨域解决方案。服务器通过设置响应头来声明允许哪些源访问资源。

// 服务器端设置
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

代理服务器

通过同源的后端服务器转发请求,绕过浏览器的同源策略限制。

// 前端请求同源服务器
fetch('/api/proxy', {
  method: 'POST',
  body: JSON.stringify({
    url: 'http://target-domain.com/api'
  })
})

// 后端Node.js示例
app.post('/api/proxy', async (req, res) => {
  const { url } = req.body;
  const response = await axios.get(url);
  res.json(response.data);
});

复杂请求的预检机制

对于可能对服务器数据产生副作用的HTTP请求方法(如PUT、DELETE等),浏览器会先发送OPTIONS请求进行预检。

// 预检请求示例
fetch('http://api.example.com/data', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'X-Custom-Header': 'value'
  },
  body: JSON.stringify({ key: 'value' })
});

服务器需要正确处理OPTIONS请求:

app.options('/data', (req, res) => {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'PUT');
  res.header('Access-Control-Allow-Headers', 'Content-Type, X-Custom-Header');
  res.sendStatus(204);
});

跨域请求中的凭证问题

默认情况下,跨域请求不会发送cookie等凭证信息。需要特殊设置:

// 前端设置
fetch('http://api.example.com/data', {
  credentials: 'include'
});

// 服务器端设置
res.header('Access-Control-Allow-Credentials', 'true');
res.header('Access-Control-Allow-Origin', 'http://your-domain.com'); // 不能是*

WebSocket跨域处理

WebSocket协议本身支持跨域,但服务器可以决定是否接受来自特定源的连接。

const socket = new WebSocket('ws://example.com/socket');

// 服务器端(Node.js + ws库)
const WebSocket = require('ws');
const wss = new WebSocket.Server({
  verifyClient: (info, done) => {
    const origin = info.origin;
    if (origin === 'http://allowed-domain.com') {
      done(true);
    } else {
      done(false, 401, 'Unauthorized');
    }
  }
});

跨域图片资源处理

Canvas操作跨域图片时需要特殊处理:

const img = new Image();
img.crossOrigin = 'Anonymous'; // 请求CORS权限
img.onload = function() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  // 现在可以安全地操作canvas了
};
img.src = 'http://example.com/image.jpg';

跨域字体加载

Web字体文件也受同源策略限制,需要在服务器设置CORS头:

@font-face {
  font-family: 'MyFont';
  src: url('http://example.com/font.woff') format('woff');
  font-display: swap;
}

服务器响应需要包含:

Access-Control-Allow-Origin: *

跨域AJAX请求的异常处理

跨域请求失败时,浏览器提供的错误信息有限,需要特别注意错误处理:

fetch('http://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    return response.json();
  })
  .catch(error => {
    console.error('Fetch error:', error);
    // 注意:跨域请求无法获取详细的错误信息
  });

本地开发环境跨域解决方案

开发时常用的解决方案:

  1. 禁用浏览器安全策略(仅限开发)

    google-chrome --disable-web-security --user-data-dir=/tmp/chrome
    
  2. 使用开发服务器代理

    // vite.config.js
    export default {
      server: {
        proxy: {
          '/api': {
            target: 'http://api.example.com',
            changeOrigin: true,
            rewrite: path => path.replace(/^\/api/, '')
          }
        }
      }
    }
    

跨域请求的性能优化

  1. 预加载CORS资源:

    <link rel="preconnect" href="https://api.example.com">
    <link rel="preload" href="https://api.example.com/data.json" as="fetch" crossorigin>
    
  2. 合并请求减少预检次数

  3. 缓存CORS预检结果:

    Access-Control-Max-Age: 86400
    

特殊场景下的跨域处理

postMessage跨文档通信

// 发送方
const popup = window.open('http://other-domain.com');
popup.postMessage('Hello', 'http://other-domain.com');

// 接收方
window.addEventListener('message', event => {
  if (event.origin !== 'http://your-domain.com') return;
  console.log('Received message:', event.data);
});

跨域iframe通信

// 父页面
document.getElementById('iframe').contentWindow.postMessage('Hello', 'http://child-domain.com');

// iframe内
window.parent.postMessage('Response', 'http://parent-domain.com');

浏览器存储的跨域限制

  1. localStorage和IndexedDB遵循同源策略
  2. 共享Worker可以有限制地跨域使用
  3. Service Worker的作用域限制
// 注册Service Worker时明确作用域
navigator.serviceWorker.register('/sw.js', {
  scope: '/app/' // 只能控制/app/路径下的请求
});

现代前端框架的跨域处理

React中的代理设置

// setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function(app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://api.example.com',
      changeOrigin: true,
    })
  );
};

Vue CLI的跨域配置

// vue.config.js
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        ws: true,
        changeOrigin: true
      }
    }
  }
}

跨域请求的安全注意事项

  1. 谨慎设置Access-Control-Allow-Origin: *
  2. 验证所有跨域请求的来源
  3. 限制允许的HTTP方法
  4. 对敏感操作实施CSRF保护
// 检查Origin头的示例中间件
app.use((req, res, next) => {
  const allowedOrigins = ['http://site1.com', 'http://site2.com'];
  const origin = req.headers.origin;
  
  if (allowedOrigins.includes(origin)) {
    res.header('Access-Control-Allow-Origin', origin);
  }
  
  next();
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:JSONP原理

下一篇:对话框方法

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌