
上期聊了认证(Authentication)和授权(Authorization)的概念区别,踩了几个坑才搞清楚这两货到底有啥不同。光看概念不过瘾,今天来点实操——手把手在 FastAPI 里搭一套完整的 JWT 认证系统。
用微信、支付宝、抖音的时候,你输入账号密码,系统验证身份,然后就能访问各种功能了。但这背后到底怎么实现的?
这篇文章来解决这个问题。
假如你在做一个 AI 辅助学习平台。没有认证的话:
这显然是个安全隐患。应用需要一种机制:
JWT 认证就是干这个的。
JWT 是 JSON Web Token(JSON 网络令牌)的缩写。
简单说,JWT 是一个安全令牌,里面包含了用户信息。用户不需要每次请求都传用户名密码,而是传一个 token 就够了。
典型流程:
注册用户 ↓ 登录 ↓
验证凭证 ↓
生成 JWT 令牌 ↓
访问受保护接口
pip install python-jose passlib[bcrypt]
用到的两个库:
passlib:处理密码哈希python-jose:生成和验证 JWT 令牌明文存储密码是极其危险的。千万别这么干:
users = { "张伟": "password123"
}
万一数据库被攻破,所有用户的密码就暴露了。正确做法是存哈希值。
from passlib.context import CryptContext pwd_context = CryptContext( schemes=["bcrypt"], deprecated="auto"
)
是密码哈希算法的管理器。这里用 指定使用 bcrypt 哈希算法。
hashed_password = pwd_context.hash("password123") print(hashed_password)
输出:
$2b$12$.....
可以看到原始密码已经看不到了。
用户登录时验证:
pwd_context.verify( "password123", hashed_password
)
返回:
True
这样就能验证密码正确性,而不需要存储明文。
写一个简单的注册接口:
from fastapi import FastAPI app = FastAPI() users = {} @app.post("/register")
def register(username: str, password: str): hashed_password = pwd_context.hash(password) users[username] = hashed_password return {"message": "用户注册成功"}
流程是这样的:用户提交用户名和密码 → 密码被哈希 → 哈希值存入数据库而非原始密码。
验证凭证:
@app.post("/login")
def login(username: str, password: str): stored_password = users.get(username) if not stored_password: return {"message": "用户不存在"} if not pwd_context.verify(password, stored_password): return {"message": "凭证无效"} return {"message": "登录成功"}
此时用户可以正常登录了。但每次请求还是得传用户名密码,JWT 要解决的就是这个问题。
from jose import jwt
from datetime import datetime, timedelta SECRET_KEY = "mysecretkey" ALGORITHM = "HS256"
密钥用来给令牌签名。如果有人篡改了令牌内容,签名就会失效。
def create_access_token(data: dict): to_encode = data.copy() expire = datetime.utcnow() + timedelta(minutes=30) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode( to_encode, SECRET_KEY, algorithm=ALGORITHM ) return encoded_jwt
函数干了四件事:复制用户数据 → 添加过期时间 → 创建带签名的 JWT → 返回令牌。
@app.post("/login")
def login(username: str, password: str): stored_password = users.get(username) if not stored_password: return {"message": "用户不存在"} if not pwd_context.verify(password, stored_password): return {"message": "凭证无效"} token = create_access_token( {"sub": username} ) return { "access_token": token, "token_type": "bearer" }
登录成功后返回:
{ "access_token": "eyJhbGciOiJIUzI1NiIs...", "token_type": "bearer"
}
@app.get("/profile")
def get_profile(): return { "message": "受保护的个人资料数据" }
现在这个接口任何人都能访问。生产环境中,FastAPI 需要在用户访问前验证 JWT 令牌。完整的接口保护逻辑下期再讲。
目前先搞清楚这几个核心流程:注册、密码哈希、密码验证、JWT 生成。这些是所有认证系统的基础。
注册用户 ↓
哈希密码 ↓
存储哈希值 ↓ 登录 ↓
验证密码 ↓
生成 JWT ↓
访问受保护接口
今天搭了一套 JWT 认证的核心组件:用户注册、密码哈希、密码验证、JWT 令牌生成。用户现在可以注册、登录、获取签名令牌。
但生成令牌只是故事的一半。下一步是学会验证令牌、用 FastAPI 依赖注入保护接口。
下期来实现基于 JWT 的接口保护,然后开始聊基于角色的访问控制(RBAC)。