FastAPI 认证与授权完全指南
FastAPI 认证与授权实践:HTTP Basic、密码哈希、Bearer JWT 校验与角色控制的局部示例,并说明令牌签发、用户库、HTTPS 和生产安全仍需补全。
直接回答:FastAPI 内置了基于 OAuth2 标准的安全工具链:从最简单的 HTTP Basic,到密码哈希存储,再到 JWT 无状态令牌认证与基于角色的授权,可以逐层搭建生产级的认证授权体系。 认证(你是谁)与授权(你能做什么)是两件事,本文分别讲清。
FastAPI 的安全框架
FastAPI 把常见认证方案封装为"可注入的依赖":OAuth2PasswordBearer、HTTPBasic、APIKeyHeader 等。它们负责提取凭据及基本格式检查,实际验签、查用户和权限判断仍由应用实现,又自动出现在生成的 OpenAPI 文档里——前端文档页就能直接登录调试,这是 FastAPI 安全体系的独特体验。
第一层:HTTP Basic 认证
from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials
import secrets
app = FastAPI()
security = HTTPBasic()
@app.get("/admin")
def admin(creds: HTTPBasicCredentials = Depends(security)):
username_ok = secrets.compare_digest(creds.username.encode("utf-8"), b"admin")
password_ok = secrets.compare_digest(creds.password.encode("utf-8"), b"s3cret")
ok = username_ok and password_ok
if not ok:
raise HTTPException(401, "认证失败", headers={"WWW-Authenticate": "Basic"})
return {"msg": "欢迎管理员"}
固定凭据仅用于本地演示;生产应校验安全存储中的密码哈希并使用 HTTPS。两个比较分别执行,Unicode 输入转 bytes,避免非 ASCII 输入触发 TypeError。Basic 认证只适合内部工具或临时方案——密码每次请求都在传输。
第二层:密码哈希与用户管理
存明文密码等于没设防。用 bcrypt 或 argon2 哈希:
from pwdlib import PasswordHash
password_hash = PasswordHash.recommended()
hashed = password_hash.hash("user-password")
ok = password_hash.verify("user-password", hashed)
用户表存哈希值;登录时验哈希,通过后签发令牌。
第三层:JWT 令牌认证
无状态认证的标配流程:登录 → 签发 JWT → 客户端携带 → 服务端验签。
from datetime import datetime, timedelta, timezone
import os
import jwt
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
SECRET = os.environ["JWT_SECRET"]
ALGO = "HS256"
def create_token(username: str) -> str:
payload = {
"sub": username,
"exp": datetime.now(timezone.utc) + timedelta(minutes=30),
}
return jwt.encode(payload, SECRET, algorithm=ALGO)
def get_current_user(token: str = Depends(oauth2_scheme)) -> str:
try:
payload = jwt.decode(token, SECRET, algorithms=[ALGO],
options={"require": ["exp", "sub"]})
subject = payload["sub"]
if not isinstance(subject, str) or not subject:
raise jwt.InvalidTokenError("invalid subject")
return subject
except jwt.PyJWTError:
raise HTTPException(401, "令牌无效或已过期", headers={"WWW-Authenticate": "Bearer"})
@app.get("/me")
def me(user: str = Depends(get_current_user)):
return {"user": user}
安装 pip install pyjwt 'pwdlib[argon2]'。这里只演示签发和校验;须另行实现 /token 登录端点,验证密码后才签发。使用高熵密钥;多签发方/受众场景还需校验 iss/aud。
第四层:接数据库与角色授权
把用户存储换成 SQLAlchemy/SQLModel 模型后,授权就是查询用户角色并校验:
def require_role(role: str):
def checker(subject: str = Depends(get_current_user)):
user = load_user_by_subject(subject) # 项目实现:查库并返回带 roles 的用户
if user is None or not user.is_active:
raise HTTPException(401, "用户不可用")
if role not in user.roles:
raise HTTPException(403, "权限不足")
return user
return checker
@app.delete("/users/{uid}")
def delete_user(uid: int, admin=Depends(require_role("admin"))):
...
依赖注入让权限规则变成可组合的装饰件——这是 FastAPI 授权体系最优雅的部分。
下一步
生产化还需补齐:HTTPS 强制、限流防爆破、审计日志、令牌吊销列表(或短过期+刷新令牌)、密钥轮换流程。
审计日志记录认证结果、拒绝原因、请求标识和操作对象,不记录密码或完整令牌。集中排查 401/403 时,可按观测云的 DataKit 日志采集说明接入该日志文件,先用一次预期拒绝的测试请求核对记录,再按接口和时间范围查询。
常见问题(FAQ)
Q:JWT 和 Session 认证怎么选?
A:JWT 无状态、适合 API 与微服务;Session 有状态、易吊销、适合传统 Web。需要"强制下线"能力的场景,JWT 要额外维护吊销列表或缩短过期时间。
Q:JWT 密钥怎么管?
A:放密钥管理系统或环境变量,绝不进代码库;生产用 RS256 非对称签名可以让验签方不接触私钥。
Q:FastAPI 的 OAuth2PasswordBearer 是完整 OAuth2 吗?
A:不是。它声明 OpenAPI 安全方案并提取 Bearer token,不负责登录、验签或签发。第三方登录通常应使用授权码与 PKCE,并接入合适的身份提供方。
官方参考
本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。