JavaScript教程:深入理解跨域请求与CORS机制

JavaScript教程:深入理解跨域请求与CORS机制

【免费下载链接】ru.javascript.info Современный учебник JavaScript 【免费下载链接】ru.javascript.info 项目地址: https://gitcode.com/gh_mirrors/ru/ru.javascript.info

引言:为什么需要跨域请求?

在现代Web开发中,前端应用经常需要从不同的域名、协议或端口获取数据。想象一下这样的场景:你的电商网站需要调用第三方支付接口,或者你的新闻应用需要聚合多个来源的内容。这就是跨域请求(Cross-Origin Requests)的典型应用场景。

然而,浏览器出于安全考虑,默认阻止了这种跨域访问。这就是**同源策略(Same-Origin Policy)**的限制。本文将深入解析跨域请求的工作原理,特别是CORS(Cross-Origin Resource Sharing,跨域资源共享)机制,帮助你彻底掌握这一关键技术。

什么是同源策略?

同源策略是浏览器最重要的安全机制之一。它规定:只有当两个URL的**协议(protocol)、域名(host)和端口(port)**完全相同时,才被认为是同源的,可以正常访问资源。

同源判断示例

// 当前页面:https://example.com:443/page.html

// 同源 ✅
https://example.com:443/other-page.html

// 不同源 ❌ - 协议不同
http://example.com:443/page.html

// 不同源 ❌ - 域名不同  
https://subdomain.example.com:443/page.html

// 不同源 ❌ - 端口不同
https://example.com:8080/page.html

跨域请求的历史演变

1. 早期解决方案:JSONP

在CORS出现之前,开发者使用JSONP(JSON with Padding)技术来处理跨域限制:

// 1. 定义回调函数
function handleWeatherData(data) {
    console.log(`温度: ${data.temperature}°C, 湿度: ${data.humidity}%`);
}

// 2. 创建script标签发起请求
const script = document.createElement('script');
script.src = 'https://api.weather.com/data?callback=handleWeatherData';
document.body.appendChild(script);

// 3. 服务器返回:handleWeatherData({temperature: 25, humidity: 78})

JSONP的局限性:

  • 仅支持GET请求
  • 错误处理困难
  • 存在安全风险(XSS攻击)

2. 服务器代理方案

另一种常见方法是通过自己的服务器代理请求:

// 前端 → 自己的服务器 → 第三方API
fetch('/api/proxy/weather')
  .then(response => response.json())
  .then(data => console.log(data));

这种方法增加了服务器负载,但提供了更好的控制和安全性。

CORS机制详解

CORS是现代浏览器支持的官方跨域解决方案,它通过在HTTP头中添加特定字段来实现安全的跨域通信。

CORS请求流程图

mermaid

1. 简单请求(Simple Requests)

简单请求必须满足以下条件:

  • 方法限制:GET、POST、HEAD
  • 头信息限制:只能包含以下头信息:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(值只能是application/x-www-form-urlencodedmultipart/form-datatext/plain

简单请求示例:

// 这是一个简单请求
fetch('https://api.example.com/data', {
  method: 'GET',
  headers: {
    'Content-Type': 'text/plain'
  }
});

2. 非简单请求(Non-Simple Requests)

任何不满足简单请求条件的请求都是非简单请求,需要先发送预检请求(Preflight Request)

非简单请求示例:

// 这是一个非简单请求(需要预检)
fetch('https://api.example.com/data', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'X-Custom-Header': 'value'
  },
  body: JSON.stringify({ key: 'value' })
});

预检请求流程

  1. 浏览器发送OPTIONS请求:
OPTIONS /data HTTP/1.1
Host: api.example.com
Origin: https://yourdomain.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: content-type,x-custom-header
  1. 服务器响应预检请求:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://yourdomain.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 86400
  1. 浏览器发送实际请求(如果预检通过)

CORS响应头详解

核心响应头

响应头说明示例
Access-Control-Allow-Origin允许访问的源https://yourdomain.com*
Access-Control-Allow-Methods允许的HTTP方法GET, POST, PUT, DELETE
Access-Control-Allow-Headers允许的自定义头Content-Type, Authorization
Access-Control-Allow-Credentials是否允许发送凭据true
Access-Control-Expose-Headers允许前端访问的响应头Content-Length, X-Custom-Header
Access-Control-Max-Age预检请求缓存时间(秒)86400

凭据(Credentials)处理

默认情况下,跨域请求不会发送cookies等凭据信息。需要显式启用:

// 前端代码
fetch('https://api.example.com/data', {
  credentials: 'include'
});

// 服务器响应必须包含
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://yourdomain.com  // 不能是 *

实际开发中的CORS配置

1. Node.js/Express配置

const express = require('express');
const app = express();

// 基础CORS配置
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://yourdomain.com');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
  res.header('Access-Control-Allow-Credentials', 'true');
  
  // 处理预检请求
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  
  next();
});

// 使用cors中间件(推荐)
const cors = require('cors');
app.use(cors({
  origin: 'https://yourdomain.com',
  credentials: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

2. Nginx配置

server {
    listen 80;
    server_name api.example.com;
    
    location / {
        # CORS配置
        add_header 'Access-Control-Allow-Origin' 'https://yourdomain.com';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE';
        add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
        add_header 'Access-Control-Allow-Credentials' 'true';
        
        # 处理预检请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Max-Age' 86400;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }
        
        # 其他代理配置
        proxy_pass http://backend;
    }
}

常见问题与解决方案

1. 预检请求失败

问题现象: 收到CORS错误,但服务器确实配置了CORS头。

解决方案:

  • 检查服务器是否正确处理OPTIONS请求
  • 确认所有需要的头信息都在Access-Control-Allow-Headers中列出

2. 凭据发送失败

问题现象: 设置了credentials: 'include'但仍然无法发送cookies。

解决方案:

  • 服务器必须设置Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Origin不能使用通配符*,必须指定具体域名

3. 响应头无法访问

问题现象: 无法通过JavaScript访问自定义响应头。

解决方案:

  • 服务器需要设置Access-Control-Expose-Headers
Access-Control-Expose-Headers: X-Custom-Header, Content-Length

安全最佳实践

1. 严格限制允许的源

// 不安全:允许所有源
Access-Control-Allow-Origin: *

// 安全:只允许特定源
const allowedOrigins = ['https://yourdomain.com', 'https://yourapp.com'];
const origin = req.headers.origin;

if (allowedOrigins.includes(origin)) {
  res.header('Access-Control-Allow-Origin', origin);
}

2. 限制允许的方法和头信息

// 只允许必要的方法和头信息
res.header('Access-Control-Allow-Methods', 'GET, POST');
res.header('Access-Control-Allow-Headers', 'Content-Type');

3. 使用预检请求缓存

// 减少预检请求频率
res.header('Access-Control-Max-Age', '86400'); // 24小时

调试技巧

1. 使用浏览器开发者工具

打开Network标签页,查看请求和响应的详细头信息:

  • 检查Origin请求头
  • 检查CORS相关响应头
  • 查看预检请求(OPTIONS)的状态

2. 使用curl测试

# 测试简单请求
curl -H "Origin: https://yourdomain.com" \
  -H "Access-Control-Request-Method: GET" \
  -X OPTIONS https://api.example.com/data \
  -I

# 测试带凭据的请求
curl -H "Origin: https://yourdomain.com" \
  -H "Cookie: session=abc123" \
  https://api.example.com/data

总结

CORS是现代Web开发中处理跨域请求的标准解决方案。通过本文的学习,你应该掌握:

  1. 同源策略的重要性:理解浏览器安全机制的设计初衷
  2. CORS的工作原理:从简单请求到预检请求的完整流程
  3. 服务器配置方法:如何在各种环境中正确配置CORS
  4. 常见问题排查:识别和解决典型的CORS相关问题
  5. 安全最佳实践:确保跨域请求的安全性

记住,CORS不是一种安全漏洞的绕过机制,而是一种受控的、安全的跨域通信方式。正确理解和应用CORS,将帮助你构建更加健壮和安全的Web应用程序。

扩展阅读

掌握跨域请求技术,让你的前端应用突破同源限制,实现更强大的功能集成和数据交互能力。

【免费下载链接】ru.javascript.info Современный учебник JavaScript 【免费下载链接】ru.javascript.info 项目地址: https://gitcode.com/gh_mirrors/ru/ru.javascript.info

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值