跳至正文

🌐 什么是CORS?

📖 定义

CORS(Cross-Origin Resource Sharing, 跨域资源共享)是一种安全机制,它告诉浏览器允许访问来自不同源(origin)的资源。出于安全原因,Web浏览器默认限制跨域资源请求,而CORS是安全允许这种访问的方法。

🎯 用类比理解

公寓安全系统

想象您住在A栋公寓:

  • 同源(Same-Origin): 从A栋101室打电话到A栋管理处 → 自由允许 ✅
  • 跨域(Cross-Origin): 从A栋101室打电话到B栋管理处 → 需要安全检查 🔒
  • CORS设置: B栋说"我也接受A栋居民的电话" → 允许通话 ✅

CORS就像这样,是访问其他建筑(源)资源时所需的许可证!

⚙️ 工作原理

1. 什么是源(Origin)?

源是协议、域名和端口的组合:

https://www.example.com:443/page
│ │ │ │ │
│ │ │ │ └─ 路径 (与源无关)
│ │ │ └────── 端口 (省略时为443)
│ │ └────────────────── 域名
│ └────────────────────── 协议
└───────────────────────────── 源 (Origin)

2. 同源 vs 跨域

// 当前页面: https://www.example.com

同源 (Same-Origin)
- https://www.example.com/page
- https://www.example.com/api/users

跨域 (Cross-Origin)
- http://www.example.com // 协议不同
- https://api.example.com // 域名不同
- https://www.example.com:8080 // 端口不同

3. CORS请求过程

Simple Request (简单请求)

1. 浏览器 → 服务器: 发送请求
GET https://api.example.com/data
Origin: https://www.mysite.com

2. 服务器 → 浏览器: 响应
Access-Control-Allow-Origin: https://www.mysite.com

3. 浏览器: 检查响应
- 如果源被允许 → 传递数据 ✅
- 如果不被允许 → CORS错误 ❌

Preflight Request (预检请求)

1. 浏览器 → 服务器: 预检 (OPTIONS)
OPTIONS https://api.example.com/data
Origin: https://www.mysite.com
Access-Control-Request-Method: POST

2. 服务器 → 浏览器: 许可响应
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: POST, GET
Access-Control-Max-Age: 3600

3. 浏览器 → 服务器: 实际请求 (POST)
POST https://api.example.com/data

4. 服务器 → 浏览器: 数据响应

💡 实际示例

CORS错误发生

// 前端 (https://www.mysite.com)
fetch('https://api.example.com/users')
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error(error));

// ❌ 控制台错误
// Access to fetch at 'https://api.example.com/users' from origin
// 'https://www.mysite.com' has been blocked by CORS policy:
// No 'Access-Control-Allow-Origin' header is present on the
// requested resource.

在服务器上启用CORS

Express.js (Node.js)

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

// 方法1: 允许所有源 (开发环境)
app.use(cors());

// 方法2: 只允许特定源 (生产环境推荐)
app.use(cors({
origin: 'https://www.mysite.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true, // 包含cookie
maxAge: 3600 // Preflight 缓存时间 (秒)
}));

// 方法3: 允许多个源
const allowedOrigins = [
'https://www.mysite.com',
'https://admin.mysite.com'
];

app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
}
}));

// API端点
app.get('/api/users', (req, res) => {
res.json({ users: ['Alice', 'Bob'] });
});

app.listen(3000);

手动设置头部

app.use((req, res, next) => {
// 允许源
res.header('Access-Control-Allow-Origin', 'https://www.mysite.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');

// 处理Preflight请求
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}

next();
});

Python Flask

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)

# 允许所有源
CORS(app)

# 只允许特定源
CORS(app, resources={
r"/api/*": {
"origins": ["https://www.mysite.com"],
"methods": ["GET", "POST"],
"allow_headers": ["Content-Type"]
}
})

@app.route('/api/users')
def get_users():
return {'users': ['Alice', 'Bob']}

Nginx配置

server {
listen 80;
server_name api.example.com;

location /api {
# 添加CORS头部
add_header 'Access-Control-Allow-Origin' 'https://www.mysite.com' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;

# 处理Preflight请求
if ($request_method = 'OPTIONS') {
return 204;
}

proxy_pass http://backend;
}
}

🤔 常见问题

Q1. 为什么需要CORS?

A: 为了安全。如果没有CORS:

// 恶意网站 (evil.com)
fetch('https://bank.com/api/transfer', {
method: 'POST',
credentials: 'include', // 包含用户的银行cookie
body: JSON.stringify({
to: 'hacker-account',
amount: 1000000
})
});

// CORS阻止了这种攻击!

Q2. 如何解决CORS错误?

A: 根据情况:

// 1. 如果可以控制服务器 → 在服务器添加CORS头部 (推荐)
app.use(cors({ origin: 'https://frontend.com' }));

// 2. 如果无法控制服务器 → 使用代理服务器
// package.json (Create React App)
{
"proxy": "https://api.example.com"
}

// 3. 开发环境 → 代理中间件
// setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');

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

// 4. 浏览器扩展程序 (仅开发!)
// 使用 "CORS Unblock" 等扩展程序
// ⚠️ 生产环境绝对不要使用!

Q3. 何时使用credentials: 'include'?

A: 发送cookie或认证信息时:

// 前端
fetch('https://api.example.com/profile', {
credentials: 'include' // 包含cookie
});

// 后端 - 必须明确指定源
app.use(cors({
origin: 'https://www.mysite.com', // 不能使用'*'!
credentials: true
}));

// ❌ 错误示例
app.use(cors({
origin: '*', // 通配符
credentials: true // 不能与credentials一起使用!
}));

Q4. Simple Request vs Preflight Request的区别?

A: 取决于条件:

// ✅ Simple Request (直接请求)
// - 方法: GET, HEAD, POST
// - 头部: 只有Accept, Content-Type等基本头部
// - Content-Type: text/plain, multipart/form-data,
// application/x-www-form-urlencoded

fetch('https://api.example.com/data', {
method: 'GET'
});

// ⚠️ Preflight Request (预检后请求)
// - 方法: PUT, DELETE, PATCH
// - 自定义头部: Authorization等
// - Content-Type: application/json

fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json', // 需要Preflight
'Authorization': 'Bearer token' // 需要Preflight
},
body: JSON.stringify({ name: 'Alice' })
});

Q5. CORS和CSRF的区别?

A: 完全不同的安全概念:

CORS (Cross-Origin Resource Sharing)
├─ 目的: 允许跨域资源访问
├─ 工作: 浏览器阻止/允许响应
└─ 解决: 在服务器添加允许头部

CSRF (Cross-Site Request Forgery)
├─ 目的: 防止伪造请求
├─ 工作: 恶意网站发送请求
└─ 解决: 使用CSRF令牌

🎓 下一步

了解CORS后,继续学习:

  1. 什么是HTTPS? (文档编写中) - 另一个重要的Web安全
  2. JWT令牌 (文档编写中) - API认证方法
  3. 什么是API? - API基础概念

调试工具

// 在浏览器开发者工具中查看
// Network选项卡 → 选择请求 → Headers选项卡

// Request Headers
Origin: https://www.mysite.com
Access-Control-Request-Method: POST

// Response Headers
Access-Control-Allow-Origin: https://www.mysite.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Max-Age: 3600

🎬 总结

CORS是Web安全的核心概念:

  • : 协议 + 域名 + 端口的组合
  • 同源策略: 默认阻止不同源
  • CORS: 安全地允许不同源
  • Preflight: 复杂请求前的预检查

当CORS错误发生时不要慌张,通过在服务器设置适当的头部来安全解决! 🌐✨