title: FastAPI安全认证:从密码到令牌的魔法之旅

date: 2025/06/02 13:24:43

updated: 2025/06/02 13:24:43

author: cmdragon

excerpt:

在FastAPI中实现OAuth2密码流程的认证机制。通过创建令牌端点,用户可以使用用户名和密码获取JWT访问令牌。代码示例展示了如何使用CryptContext进行密码哈希处理,生成和验证JWT令牌,并实现安全路由保护。此外,还提供了JWT令牌的结构解析、常见报错解决方案以及安全增强建议,如使用HTTPS传输令牌和从环境变量读取密钥。最后,通过课后Quiz巩固了关键概念。

categories:

  • 后端开发
  • FastAPI

tags:

  • FastAPI
  • OAuth2
  • JWT
  • 安全认证
  • 密码哈希
  • 令牌校验
  • 访问控制


扫描二维码

关注或者微信搜一搜:编程智域 前端至全栈交流与成长

探索数千个预构建的 AI 应用,开启你的下一个伟大创意https://tools.cmdragon.cn/

第一章:FastAPI安全认证核心实现

(注:根据写作规范要求,章节编号从第一章开始编排)

一、令牌端点(Token Endpoint)的创建

1.1 OAuth2密码流程原理

OAuth2密码流程(Password Grant)是直接通过用户名密码获取访问令牌的认证方式。类比演唱会验票流程:用户先到售票处(令牌端点)用身份证(凭证)换取门票(令牌),之后凭门票入场(访问资源)。

流程步骤:

  1. 客户端发送用户名密码到/token端点
  2. 服务器验证凭证有效性
  3. 生成包含用户身份和有效期的JWT令牌
  4. 返回访问令牌给客户端

1.2 FastAPI端点实现

from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel
from datetime import datetime, timedelta
from jose import JWTError, jwt
from passlib.context import CryptContext router = APIRouter(tags=["Authentication"]) # 密码哈希配置(使用bcrypt算法)
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") # JWT配置(实际项目应从环境变量读取)
SECRET_KEY = "your-secret-key-keep-it-secret!"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # 用户模型
class UserCreate(BaseModel):
username: str
password: str # 令牌响应模型
class Token(BaseModel):
access_token: str
token_type: str @router.post("/token", response_model=Token)
async def login_for_access_token(form_data: UserCreate):
# 用户验证(示例用静态数据,实际应查数据库)
if form_data.username != "admin" or not pwd_context.verify(
"secret", # 数据库中存储的哈希密码
form_data.password
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="用户名或密码错误",
headers={"WWW-Authenticate": "Bearer"},
) # 生成JWT令牌
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": form_data.username},
expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"} def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

代码解析:

  1. CryptContext 使用bcrypt算法进行密码哈希处理
  2. UserCreate模型规范了客户端请求的数据格式
  3. 密码验证使用verify()方法比对哈希值
  4. create_access_token生成带过期时间的JWT令牌

1.3 运行环境配置

# 安装依赖库(版本需严格对应)
fastapi==0.68.1
uvicorn==0.15.0
python-jose[cryptography]==3.3.0
passlib==1.7.4

二、访问令牌生成与校验

2.1 JWT令牌结构解析

示例令牌:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTY1OTA3MDQwMH0.3w7hJH4KZ6Q-Mje3Q2T3T6k4Vd6QyQ6Qk7v6Qw7q6Qk

分段说明:

  • Header:{"alg": "HS256", "typ": "JWT"}
  • Payload:{"sub": "admin", "exp": 1659070400}
  • Signature:使用密钥对前两部分的签名

2.2 令牌校验实现

from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")

async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="无法验证凭证",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception # 此处应查询数据库验证用户存在性
if username != "admin":
raise credentials_exception
return username # 安全路由示例
@router.get("/protected")
async def protected_route(current_user: str = Depends(get_current_user)):
return {"message": f"欢迎您, {current_user}"}

校验流程:

  1. 从请求头提取Bearer令牌
  2. 解码并验证JWT签名
  3. 检查令牌有效期
  4. 验证用户是否存在(示例简化处理)

三、课后Quiz

  1. 为什么在密码存储时要使用哈希而不是明文?

    A. 提高查询速度

    B. 防止数据泄露导致密码暴露

    C. 减少存储空间占用

    D. 方便密码找回

    答案:B。哈希处理后的密码即使泄露也无法逆向获取原始密码

  2. JWT中的签名部分主要作用是什么?

    A. 美化令牌格式

    B. 验证令牌内容未被篡改

    C. 加速令牌解析

    D. 支持多种加密算法

    答案:B。签名确保令牌在传输过程中未被修改

四、常见报错解决方案

问题1:401 Unauthorized

  • 现象{"detail":"Not authenticated"}
  • 原因:请求头缺少Authorization字段或格式错误
  • 解决
    curl -H "Authorization: Bearer your_token" http://localhost:8000/protected

问题2:422 Validation Error

  • 现象:请求体参数校验失败
  • 原因:未按UserCreate模型格式提交数据
  • 解决:检查请求是否包含username和password字段

问题3:403 Forbidden

  • 现象{"detail": "Invalid authentication credentials"}
  • 原因:令牌已过期或签名验证失败
  • 解决:重新获取有效令牌,检查密钥一致性

五、安全增强建议

  1. 生产环境必须

    • 通过HTTPS传输令牌
    • 使用环境变量存储密钥
    • 定期轮换加密密钥
  2. 推荐方案
    # 从环境变量读取密钥
    import os
    SECRET_KEY = os.getenv("JWT_SECRET_KEY")
    if not SECRET_KEY:
    raise ValueError("Missing JWT_SECRET_KEY environment variable")

通过本章学习,开发者可以掌握FastAPI的OAuth2密码流程核心实现,建议结合数据库实现完整的用户管理系统。下一章将讲解权限控制与角色管理的高级应用。

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:FastAPI安全认证:从密码到令牌的魔法之旅 | cmdragon's Blog

往期文章归档:

FastAPI安全认证:从密码到令牌的魔法之旅的更多相关文章

  1. ACL认证 vs 密码认证

    呼入时需要进行认证:acl IP认证 和 密码认证.  acl 认证优先进行. ACL认证成功: Access Granted.  直接进入 sip_profile>context 进行路由 A ...

  2. redis配置密码认证,通过密码可以进行连接

    需求说明: 今天配置了一台redis服务器,想要也和其他的数据库一样配置用户名/密码的方式进行登录. 查找了一下,没看到配置用户名的地方,就是有认证密码,所以就做了测试,在此进行记录. 操作过程: 1 ...

  3. 3.jenkins 权限认证与密码设置

    1.前言 在用Jenkins过程中忘记管理员密码和开启权限认证后管理员帐号没有任何权限是经常遇到的情况,最近有好多群友被这个问题困扰.但Jenkins没有提供密码找回的功能,经过一翻探索找到了一种变相 ...

  4. htpasswd.cgi 网页远程修改gerrit ht 认证的密码文件

    在搭建gerrit系统时,一般都会采用apache的.htacces 认证方法 但trac本身并不提供修改密码的功能,修改密码只能通过htpasswd/htpasswd2命令来进行,这的确是一件相当不 ...

  5. linux ssh密钥认证, 免密码登陆

    1. 客户端生成密钥 # mkdir ~/.ssh # chmod ~/.ssh # cd ~/.ssh 生成RSA密钥 # ssh-keygen -t rsa (然后连续三次回车) 2. 把公钥传到 ...

  6. Kubernetes-Secret

    1. 简介 Secret 是一种包含少量敏感信息例如密码.令牌或密钥的对象. 这样的信息可能会被放在 Pod 规约中或者镜像中. 使用 Secret 意味着你不需要在应用程序代码中包含机密数据. 由于 ...

  7. ASP.NET Core的身份认证框架IdentityServer4--(3)令牌服务配置访问控制跟UI添加

    使用密码保护API OAuth 2.0 资源所有者密码授权允许一个客户端发送用户名和密码到IdentityServer并获得一个表示该用户的可以用于访问api的Token. 该规范建议仅对" ...

  8. ssh密码登录+ Google Authenticator 实现双向认证

    通常我们直接通过ssh输入密码连接服务器,但这样很容易出现暴力破解情况,所以我们可以结合google的动态认证+ssh密码,这样能够大大的提升登陆的安全. 简单来说,就是当用户通过ssh登陆系统时,先 ...

  9. 射频识别技术漫谈(19)——Desfire的3次握手认证和段密码生成

    3次握手认证并生成临时的通讯密钥在通讯技术中的应用非常普遍,Mifare Desfire也使用了这种成熟的认证加密方法.Desfire在卡片数据传输前使用DES或3DES进行3次握手认证,认证成功一方 ...

  10. Cognos权限认证CJP方式之用户密码加密

    在项目开发过程中,用户往往对系统的安全都有明确的要求,下面针对cognos门户认证用户密码如何加密来提供一个简单的wf 1Cognos权限认证方式:CJP 2Cognos用户数据库类型:Oracle ...

随机推荐

  1. Qt 给窗口绘制阴影

    文章目录 Qt 给窗口绘制阴影 前言 重载`paintEvent`法 QGraphicsDropShadowEffect方法 使用九图拼凑法 九宫格缩放阴影法 Qt 给窗口绘制阴影 前言 最近自定义一 ...

  2. Jupyter Notebook的所有文件ipynb保存下来

    前言 如果你想要保存整个 Jupyter Notebook 工作目录,包括所有笔记本和其他相关文件,最直接的方法是将整个文件夹压缩为一个 ZIP 或 TAR 文件. 下载单个文件 压缩文件夹下载 在 ...

  3. go mgo包 简单封装 mongodb 数据库驱动

    mgo是go编写的mongodb的数据库驱动,集成到项目中进行mongodb的操作很流畅,以下是对其的一些简单封装,具体使用可随意改动封装. 安装 go get gopkg.in/mgo.v2 使用 ...

  4. go cobra Error: required flag(s) "pkg-name" not set

    Cobra 是一个 Golang 包,它提供了简单的接口来创建命令行程序.同时,Cobra 也是一个应用程序,用来生成应用框架,从而开发以 Cobra 为基础的应用. 使用 cobra init 命令 ...

  5. 1、从DeepSeek API调用到Semantic Kernel集成:深度解析聊天机器人开发全链路

    引言:AI时代下的聊天机器人开发范式演进 在生成式AI技术爆发的当下,基于大语言模型(LLM)的聊天机器人开发已形成标准化技术链路.本文将结合DeepSeek API与微软Semantic Kerne ...

  6. 【ffmpeg】avformat_alloc_context报错System.NotSupportedException不支持所指定的方法

    这个错误报了第二次了,网上搜不到靠谱的解决方案,赶快记录一下. 第一个情况:报错如题目System.NotSupportedException 不支持所指定的方法 第二个情况:如果换autogen版本 ...

  7. 寻找可靠的长久的存储介质之旅,以及背后制作的三个网页“图片粘贴转base64”、“生成L纠错级别的QR码”、“上传文件转 base64以及粘贴 base64 转可下载文件”

    其实对于目前的形式来说,虽然像 U 盘.固态硬盘.甚至光盘这些信息储存介质(设备)的容量越来越高,但是不得不说这些设备的可靠性依然像悬着的一块石头,虽然这块石头确实牢牢的粘在天花板上,但是毕竟是粘上去 ...

  8. 探秘Transformer系列之(18)--- FlashAttention

    探秘Transformer系列之(18)--- FlashAttention 目录 0x00 概述 0.1 问题 0.2 其它解决方案 0.3 Flash Attention 0x01 背景知识 1. ...

  9. spring 事务失效的 12 种场景

    看这个:https://blog.csdn.net/hanjiaqian/article/details/120501741里面有12种失效场景以及如何解决. 在 spring 中为了支持编程式事务, ...

  10. ASP.NET Core 全球化和本地化

    留备后观... Globalization and localization in ASP.NET Core 体验 ASP.NET Core 中的多语言支持(Localization)