FastAPI CORS 跨域
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种安全机制,允许或限制网页从不同域名请求资源。当前后端分离开发时,前端页面通常运行在不同的域名或端口上,需要配置 CORS 才能正常访问后端 API。
什么是跨域问题
浏览器的同源策略限制了网页向不同域名发送请求。例如:
| 前端地址 | 后端 API 地址 | 是否跨域 |
|---|---|---|
http://localhost:3000 | http://localhost:8000 | 跨域(端口不同) |
http://example.com | http://api.example.com | 跨域(域名不同) |
https://example.com | http://example.com | 跨域(协议不同) |
http://example.com | http://example.com/api | 同源(相同域名、端口、协议) |
同源的三要素:协议、域名、端口,任一不同即为跨域。
配置 CORS
FastAPI 使用 Starlette 的 CORSMiddleware 来处理跨域:
实例
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置 CORS 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=[ # 允许的源(域名列表)
"http://localhost:3000", # 前端开发服务器
"http://localhost:8080",
],
allow_credentials=True, # 允许携带 Cookie
allow_methods=["*"], # 允许的 HTTP 方法
allow_headers=["*"], # 允许的请求头
)
@app.get("/")
async def root():
return {"message": "Hello World"}
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 配置 CORS 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=[ # 允许的源(域名列表)
"http://localhost:3000", # 前端开发服务器
"http://localhost:8080",
],
allow_credentials=True, # 允许携带 Cookie
allow_methods=["*"], # 允许的 HTTP 方法
allow_headers=["*"], # 允许的请求头
)
@app.get("/")
async def root():
return {"message": "Hello World"}
CORS 配置参数详解
| 参数 | 类型 | 说明 | 推荐值 |
|---|---|---|---|
allow_origins | list[str] | 允许跨域的源列表 | 生产环境用具体域名 |
allow_methods | list[str] | 允许的 HTTP 方法 | ["GET", "POST", "PUT", "DELETE"] |
allow_headers | list[str] | 允许的请求头 | 按需配置 |
allow_credentials | bool | 是否允许携带 Cookie 和认证信息 | 需要认证时设为 True |
expose_headers | list[str] | 前端可以访问的响应头 | 按需配置 |
max_age | int | 预检请求的缓存时间(秒) | 600 |
allow_origins使用["*"]表示允许所有源,但这与allow_credentials=True不兼容。如果需要携带 Cookie,必须指定具体的源。
开发环境 vs 生产环境
开发环境
开发时为了方便,可以允许所有源:
实例
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 开发环境:允许所有源(仅用于开发!)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有源
allow_credentials=True,
allow_methods=["*"], # 允许所有方法
allow_headers=["*"], # 允许所有请求头
)
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 开发环境:允许所有源(仅用于开发!)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # 允许所有源
allow_credentials=True,
allow_methods=["*"], # 允许所有方法
allow_headers=["*"], # 允许所有请求头
)
生产环境
生产环境必须指定具体的源:
实例
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 生产环境:只允许特定域名
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://example.com", # 生产前端域名
"https://www.example.com", # 带 www 的域名
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
max_age=600, # 预检请求缓存 10 分钟
)
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
# 生产环境:只允许特定域名
app.add_middleware(
CORSMiddleware,
allow_origins=[
"https://example.com", # 生产前端域名
"https://www.example.com", # 带 www 的域名
],
allow_credentials=True,
allow_methods=["GET", "POST", "PUT", "DELETE"],
allow_headers=["Authorization", "Content-Type"],
max_age=600, # 预检请求缓存 10 分钟
)
在生产环境中使用
allow_origins=["*"]是安全隐患,可能导致跨站请求伪造(CSRF)攻击。务必指定具体的前端域名。
CORS 预检请求
浏览器在发送某些跨域请求前,会先发送一个 OPTIONS 预检请求(preflight request),询问服务器是否允许实际请求。CORS 中间件会自动处理预检请求。
需要预检的请求条件:
- 使用了
PUT、DELETE等非简单方法 - 请求头包含自定义字段(如
Authorization) Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain
设置了
max_age后,浏览器会缓存预检结果,在指定时间内不会重复发送预检请求,减少网络开销。
小结
- CORS 是浏览器安全机制,前后端分离开发时必须配置
- 使用
CORSMiddleware处理跨域请求 - 生产环境务必指定具体的
allow_origins,不要使用["*"] allow_credentials=True与allow_origins=["*"]不兼容- CORS 中间件自动处理浏览器的 OPTIONS 预检请求
