1. 環境準備與依賴安裝¶
要實現FastAPI+JWT的用戶登錄驗證,我們需要先安裝必要的Python庫。打開終端,執行以下命令:
pip install fastapi uvicorn python-jose[cryptography] passlib[bcrypt] python-multipart
- FastAPI:高性能Web框架,用於構建API
- uvicorn:ASGI服務器,用於運行FastAPI應用
- python-jose:用於生成和驗證JWT令牌
- passlib:用於密碼哈希處理(安全存儲密碼)
- bcrypt:passlib的依賴,用於密碼加密算法
- python-multipart:處理表單數據(登錄接口常用)
2. 核心概念理解¶
- JWT(JSON Web Token):一種無狀態的身份驗證方式,服務器通過用戶信息生成加密令牌,客戶端每次請求攜帶令牌即可證明身份。
- 依賴項(Dependency):FastAPI中用於複用邏輯(如驗證Token)的機制,可自動處理請求並返回結果。
- 密碼哈希:登錄時存儲的密碼不直接保存明文,而是通過算法(如bcrypt)轉爲不可逆的哈希值,驗證時通過比對哈希值判斷密碼是否正確。
3. 代碼實現步驟¶
3.1 導入必要庫¶
from datetime import datetime, timedelta
from typing import Optional
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from pydantic import BaseModel
3.2 配置JWT參數¶
# 配置JWT相關參數(生產環境需改爲安全存儲,如環境變量)
SECRET_KEY = "your-secret-key-keep-it-safe-and-long-enough" # 建議16位以上隨機字符串
ALGORITHM = "HS256" # 加密算法(HS256=HMAC-SHA256)
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Token有效期30分鐘
3.3 模擬用戶數據庫¶
用列表模擬數據庫存儲用戶信息(生產環境建議使用真實數據庫如SQLite/PostgreSQL):
# 模擬用戶數據(實際需替換爲數據庫查詢)
fake_users_db = {
"johndoe": {
"username": "johndoe",
"hashed_password": "hashed_secret_password", # 後續用bcrypt生成
"disabled": False,
}
}
3.4 密碼哈希工具(passlib)¶
使用bcrypt算法對密碼進行哈希處理:
# 初始化密碼哈希上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def get_password_hash(password: str) -> str:
"""將密碼轉爲哈希值(存儲)"""
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""驗證密碼是否匹配哈希值"""
return pwd_context.verify(plain_password, hashed_password)
3.5 JWT工具函數(生成與驗證Token)¶
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""生成JWT Token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
def verify_token(token: str, credentials_exception) -> dict:
"""驗證Token有效性"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub") # 從Token中提取用戶名(sub=subject)
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
return {"username": username}
3.6 OAuth2依賴項(提取Token)¶
FastAPI提供OAuth2PasswordBearer工具自動從請求頭獲取Token:
# OAuth2方案:自動從Header獲取Bearer Token
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
3.7 定義用戶模型¶
用Pydantic定義請求/響應數據結構(保證數據格式正確):
class User(BaseModel):
username: str
disabled: Optional[bool] = None
class UserInDB(User):
hashed_password: str
3.8 登錄接口(獲取Token)¶
接收用戶名/密碼,驗證後返回Token:
# 登錄接口:路徑爲/token,接收表單參數(username/password)
@app.post("/token")
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(), db=fake_users_db):
# 1. 從數據庫查用戶(實際需替換爲查詢邏輯)
user = db.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 2. 驗證密碼
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# 3. 生成Token
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"]}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
3.9 受保護接口(驗證Token)¶
用依賴項驗證Token後返回用戶信息:
# 依賴項:驗證Token並返回用戶
async def get_current_user(
token: str = Depends(oauth2_scheme),
credentials_exception=HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
),
):
user = verify_token(token, credentials_exception)
return User(username=user["username"])
# 受保護路由示例
@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_user)):
return {"user": current_user}
3.10 主程序入口¶
app = FastAPI()
# 初始化哈希密碼(生產環境中用戶註冊時調用,此處爲演示先手動哈希)
if __name__ == "__main__":
# 手動生成測試用戶的哈希密碼(實際應在註冊接口中調用)
test_password = "testpassword"
fake_users_db["johndoe"]["hashed_password"] = get_password_hash(test_password)
4. 運行與測試¶
- 啓動服務:在代碼文件(如
main.py)所在目錄執行:
uvicorn main:app --reload
- 訪問Swagger UI:打開瀏覽器訪問
http://localhost:8000/docs,即可在界面中測試登錄接口。 - 測試流程:
- 調用POST /token接口,輸入username=johndoe、password=testpassword,獲取access_token。
- 用Token調用GET /users/me接口,在請求頭中添加Authorization: Bearer <token>,即可返回用戶信息。
5. 關鍵知識點總結¶
- 依賴項(Dependency):複用Token驗證邏輯,自動返回用戶信息或攔截無效請求。
- JWT安全性:需保護
SECRET_KEY(生產環境用環境變量存儲),避免Token過期或泄露。 - 密碼存儲:用
passlib哈希算法存儲密碼,禁止明文存儲,防止數據泄露風險。
通過以上步驟,你已實現一個基於FastAPI和JWT的基礎用戶登錄驗證系統。後續可擴展功能如刷新Token、多用戶角色權限等。