Skip to content

CORS跨域

CORS(Cross-Origin Resource Sharing)跨域资源共享是前后端分离架构必须处理的问题。项目使用 django-cors-headers 中间件处理跨域请求,通过 .env 配置允许的跨域源。

什么是跨域

浏览器的同源策略限制了不同源(协议+域名+端口)之间的请求。前后端分离架构中,前端(如 http://localhost:8001)请求后端(如 http://127.0.0.1:8000)会产生跨域问题。

同源请求:http://localhost:8000/api/user → http://localhost:8000/api/role ✅
跨域请求:http://localhost:8001/api/user → http://localhost:8000/api/role ❌(端口不同)

CORS 预检请求

对于非简单请求(如 PUT/DELETE 或自定义 Header),浏览器会先发送 OPTIONS 预检请求:

浏览器 → OPTIONS /api/user → 服务器
浏览器 ← Access-Control-Allow-Origin: * ← 服务器
浏览器 → PUT /api/user → 服务器(正式请求)

预检请求

django-cors-headers 中间件自动处理 OPTIONS 预检请求,无需手动编写代码。预检请求不会到达视图层,由中间件直接返回。

配置项

settings.py 中配置 CORS 相关参数:

python
# application/settings.py
INSTALLED_APPS = [
    ...
    'corsheaders',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # 必须在最前面
    'django.middleware.common.CommonMiddleware',
    ...
]

# CORS 配置
CORS_ALLOW_ALL_ORIGINS = True  # 开发环境允许所有源
CORS_ALLOW_CREDENTIALS = True  # 允许携带凭证(Cookie/Authorization)
CORS_ALLOW_METHODS = [         # 允许的请求方法
    'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT',
]
CORS_ALLOW_HEADERS = [         # 允许的请求头
    'accept-encoding',
    'authorization',           # JWT 认证头
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
    'XMLHttpRequest',
    'X_FILENAME',
    'Pragma',
]

环境配置

bash
# .env

# 开发环境:允许所有源
CORS_ALLOW_ALL_ORIGINS=True

# 生产环境:指定域名
# CORS_ALLOW_ALL_ORIGINS=False
# CORS_ALLOWED_ORIGINS=https://admin.example.com,https://www.example.com

生产环境

生产环境务必设置 CORS_ALLOW_ALL_ORIGINS=False 并配置 CORS_ALLOWED_ORIGINS 为具体域名,避免允许所有源带来的安全风险。

生产环境配置

多域名配置

python
# 生产环境:指定多个允许的域名
CORS_ALLOWED_ORIGINS = [
    "https://admin.example.com",
    "https://www.example.com",
    "https://m.example.com",
]

正则匹配

python
# 允许所有子域名
CORS_ALLOWED_ORIGIN_REGEXES = [
    r"^https://.*\.example\.com$",
]

禁止凭证

如果不需要携带 Cookie,可以关闭凭证以提高安全性:

python
CORS_ALLOW_CREDENTIALS = False  # 不允许携带凭证

中间件顺序

CORS 中间件必须在 MIDDLEWARE 列表中最前面注册:

python
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',  # ← 必须在最前面
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
]

顺序重要

CorsMiddleware 必须在所有中间件之前,特别是要在 CommonMiddleware 之前。否则 OPTIONS 预检请求可能被 CommonMiddleware 拦截,导致 CORS 头未正确添加。

前端代理配置

开发环境通过 Vite 代理解决跨域问题(无需 CORS):

javascript
// ui/vite.config.ts
server: {
    proxy: {
        '/api': {
            target: 'http://127.0.0.1:8000',
            changeOrigin: true,
        }
    }
}

开发环境 vs 生产环境

  • 开发环境:Vite 代理将 /api 请求转发到后端,浏览器视为同源请求,不触发 CORS
  • 生产环境:Nginx 统一处理前端静态资源和 API 转发,同样不触发 CORS
  • 跨域场景:当前端和后端部署在不同域名时,才需要 CORS 配置

常见问题排查

问题 1:请求被 CORS 拦截

现象:浏览器控制台报 Access-Control-Allow-Origin 错误

排查

bash
# 1. 检查 CORS 中间件是否在最前面
grep -n "CorsMiddleware" application/settings.py

# 2. 检查 CORS 配置
grep -n "CORS_ALLOW" application/settings.py

# 3. 检查 .env 中的配置
cat .env | grep CORS

问题 2:OPTIONS 预检请求返回 403

现象:OPTIONS 请求被权限中间件拦截

排查

bash
# 检查 check_login 是否排除了 OPTIONS 请求
# middleware/login_middleware.py 中应放行 OPTIONS

问题 3:携带自定义 Header 失败

现象:自定义 Header(如 X-Token)被拒绝

解决

python
# 在 CORS_ALLOW_HEADERS 中添加自定义 Header
CORS_ALLOW_HEADERS = [
    ...
    'x-token',
    'custom-header',
]

现象:跨域请求中 Cookie 未携带

解决

python
# 1. 后端设置
CORS_ALLOW_CREDENTIALS = True

# 2. 前端设置(Axios)
axios.defaults.withCredentials = true

# 3. 注意:Allow-Credentials 为 true 时,Allow-Origin 不能为 *

安全建议

1. 生产环境禁止 CORS_ALLOW_ALL_ORIGINS = True
2. 只允许必要的域名和请求方法
3. 谨慎使用 CORS_ALLOW_CREDENTIALS = True
4. 定期审查 CORS 配置,移除不再使用的域名
5. 避免在 CORS_ALLOW_HEADERS 中使用通配符 *

总结

CORS 跨域配置具备以下特点:

1. django-cors-headers:成熟的 Django CORS 解决方案
2. 环境变量配置:通过 .env 灵活配置允许的源
3. 开发/生产分离:开发环境允许所有源,生产环境指定域名
4. 中间件顺序:必须在 CommonMiddleware 之前
5. 前端代理:开发环境通过 Vite 代理解决跨域
6. 预检处理:自动处理 OPTIONS 预检请求
7. 安全可控:支持域名白名单、正则匹配、凭证控制

小蚂蚁云团队 · 提供技术支持