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,并接入合适的身份提供方。

官方参考

本文基于官方文档整理,未进行运行时或性能测试。示例中的业务函数、数据模型和部署地址需结合项目补全;局部片段不等同于完整生产应用。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台