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. 【Jmatpro 10.0】根据材料牌号输出应力-应变曲线

    1.前提条件 Jmatpro 软件 一个材料牌号或者材料成分数据 2. 步骤 以 X15Cr 13牌号金属为案例 材料数据来自:材数库 运行jmatpro软件 选择 material type:sta ...

  2. linux ubuntu mysql 默认密码

    前言 linux ubuntu安装mysql Ubuntu 安装 MySQL 后,在 /etc/mysql 目录下有个 debian.cnf,这里存储着默认的密码. [client] host = l ...

  3. Joker 可视化开发平台全局方法使用指南

    在 Joker 可视化开发平台中,全局方法是实现公共业务逻辑的有力工具,它能跨越组件和页面文件的界限,让开发者快速调用,显著提升开发效率.下面将详细介绍全局方法在平台中的使用方式. 一.全局方法的定义 ...

  4. 【Linux】5.4 Shell数组

    Shell数组 数组中可以存放多个值.Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似). 1. 数组赋值 与大部分编程语言类似,数组元素的下标由0 ...

  5. 【SpringCloud】Zookeeper服务注册与发现

    Zookeeper服务注册与发现 Eureka停止更新了,你怎么办 https://github.com/Netflix/eureka/wiki SpringCloud整合Zookeeper替代Eur ...

  6. kubectl

    ... Nodes k8s查看节点CPU消耗情况,可以用kubectl top命令,但是会出现 kubectl top nodes error: Metrics API not available 退 ...

  7. AspNetCore MVC 跨域

    通过XMLHttpRequest或者ajax去请求一个AspNetCore API接口服务时,Firefox提示我 已拦截跨源请求:同源策略禁止读取位于 http://localhost:33694/ ...

  8. REST API从木愣到够呆

    目前准备写一个API服务,遵循REST规范,因为自己也是第一次接触这玩意,所以就以自我的认知和理解过程来记录,留爪. ##在REST里什么叫资源? 拿数据表为例: 现在有三张表:(此表非彼婊) sch ...

  9. unigui的ServerModule的重要属性【8】

    ServerModule是unigui的重要模块. uniGUI 服务器的内部结构. 每个 uniGUI 服务器都有一个ServerModule的副本, 每台服务器创建一次, 同时根据用户活动动态创建 ...

  10. # 🤖 **DeepSeek 深度解析 PasteForm:一个让管理端开发爽到飞起的全栈解决方案**

    DeepSeek 深度解析 PasteForm:一个让管理端开发爽到飞起的全栈解决方案 各位开发者注意啦!今天我要带大家全方位解剖 PasteForm 这个神奇框架--不仅介绍核心思想,更要重点展示它 ...