【FastAPI 學習筆記 EP.17】JWT (JSON Web Tokens) 生成

更新 發佈閱讀 11 分鐘

什麼是 JWT (JSON Web Tokens)?

JWT (JSON Web Tokens) 是一種開放標準,用於在各方之間以 JSON 物件安全地傳輸資訊,透過數位簽章確保資料在傳輸過程中未被篡改,伺服器只需驗證即可,無需每次都向資料庫查核身分。

JWT (JSON Web Tokens) 基礎語法

  1. 安裝依賴套件:需要安裝 pip install "passlib[argon2]" pyjwt 處理加密簽章與處理密碼雜湊
  2. 定義 OAuth2:使用 OAuth2PasswordBearer 定義 Token 的獲取路徑(tokenUrl)
  3. jwt.encode:使用 SECRET_KEY 與演算法 (例如 HS256) 將使用者資訊與過期時間轉換成 Token
  4. jwt.decode:解碼 Token 並驗證簽章是否有效、是否過期,若失敗則拋出異常
  5. 保護路由:利用 Depends 將驗證函式注入到路由參數,確保請求必須攜帶有效 Token 才能執行
from datetime import datetime, timedelta, timezone
from typing import Annotated, Optional
import jwt
from jwt.exceptions import InvalidTokenError
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from passlib.context import CryptContext
from pydantic import BaseModel

# 在真實環境中,以下變數應由環境變數 (.env) 讀取,嚴禁寫死在程式碼中
SECRET_KEY = "my_super_secure_secret_key_for_demo_only"
ALGORITHM = "HS256"
TOKEN_EXPIRE_MINUTES = 30

app = FastAPI(title="JWT Auth Demo (PyJWT Version)")

# 密碼雜湊工具 (使用 argon2 演算法,需安裝 passlib[argon2])
pwd_context = CryptContext(schemes=["argon2"], deprecated="auto")

# 定義 OAuth2 流程,'tokenUrl' 必須對應下方登入路由的 path
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="auth/login")

# 用於回應給前端的使用者資料 (不含密碼)
class UserPublic(BaseModel):
username: str
email: Optional[str] = None

# 資料庫內部的使用者結構 (包含雜湊密碼)
class UserPrivate(UserPublic):
password_hash: str

# Token 回應結構
class TokenResponse(BaseModel):
access_token: str
token_type: str

# --- 模擬資料庫 ---
MOCK_USER_DB = {
"admin": {
"username": "admin",
"email": "[email protected]",
"password_hash": pwd_context.hash("pass123"),
}
}

def create_jwt_token(data: dict, expires_delta: timedelta | None = None):
"""
生成帶有過期時間的 JWT 字串
"""
to_encode = data.copy()

if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)

to_encode.update({"exp": expire})

# 使用 PyJWT 進行編碼
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]) -> UserPrivate:
"""
驗證 Token 的依賴函式 (Dependency)。
解析 Token -> 檢查過期與正確性 -> 撈取使用者
"""
auth_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="無法驗證憑證",
headers={"WWW-Authenticate": "Bearer"},
)

try:
# decode 會自動驗證 exp (過期時間) 和簽章
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")

if username is None:
raise auth_exception

except InvalidTokenError:
raise auth_exception

# 確認使用者是否存在於資料庫
user_data = MOCK_USER_DB.get(username)
if user_data is None:
raise auth_exception

return UserPrivate(**user_data)

@app.post("/auth/login", response_model=TokenResponse)
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
):
"""
登入路由:接收 form-data (username, password),回傳 JWT。
"""
user = MOCK_USER_DB.get(form_data.username)

# 驗證帳號是否存在 以及 密碼是否匹配
if not user or not pwd_context.verify(form_data.password, user["password_hash"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="帳號或密碼錯誤",
headers={"WWW-Authenticate": "Bearer"},
)

# 製作 Token
access_token_expires = timedelta(minutes=TOKEN_EXPIRE_MINUTES)
access_token = create_jwt_token(
data={"sub": user["username"]},
expires_delta=access_token_expires
)

return {"access_token": access_token, "token_type": "bearer"}

@app.get("/users/me", response_model=UserPublic)
async def read_current_user(
current_user: Annotated[UserPrivate, Depends(get_current_user)]
):
"""
受保護的路由:必須攜帶有效 Token 才能訪問。
Response Model 會自動過濾掉 UserPrivate 中的 password_hash
"""
return current_user
留言
avatar-img
梧笙 WuSheng 的沙龍
65會員
17內容數
⛰️ 梧笙,意即「吾生」,我是一個平凡的理工宅男。 生活離不開 Code 與 Game,這裡主要紀錄與分享: 📖 學習筆記|紀錄我學習過的電腦技能與知識。 💻 科技新知|整理實用工具與科技領域的資訊。 🎮 電玩娛樂|聊聊遊戲動漫與實況直播的話題。 目前更新頻率不固定,有興趣歡迎追蹤。
2026/04/14
OAuth2 是一種標準授權框架,而 Password Flow 是其中一種允許使用者直接使用帳號密碼換取存取權杖 (Access Token) 的模式。
Thumbnail
2026/04/14
OAuth2 是一種標準授權框架,而 Password Flow 是其中一種允許使用者直接使用帳號密碼換取存取權杖 (Access Token) 的模式。
Thumbnail
2026/04/14
密碼雜湊 (Password Hashing) 是指將明文密碼轉換為不可逆的雜湊值,確保即使資料庫外洩,攻擊者也無法還原原始密碼。通常應用於使用者註冊時的密碼儲存,以及登入時的密碼驗證流程。
Thumbnail
2026/04/14
密碼雜湊 (Password Hashing) 是指將明文密碼轉換為不可逆的雜湊值,確保即使資料庫外洩,攻擊者也無法還原原始密碼。通常應用於使用者註冊時的密碼儲存,以及登入時的密碼驗證流程。
Thumbnail
2026/01/06
FastAPI 會提供 OpenAPI Schema,並內建互動式文件介面 Swagger UI,預設路徑為 /docs,可直接在瀏覽器測試 API。
Thumbnail
2026/01/06
FastAPI 會提供 OpenAPI Schema,並內建互動式文件介面 Swagger UI,預設路徑為 /docs,可直接在瀏覽器測試 API。
Thumbnail
看更多
你可能也想看
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
本文分析導演巴里・柯斯基(Barrie Kosky)如何運用極簡的舞臺配置,將布萊希特(Bertolt Brecht)的「疏離效果」轉化為視覺奇觀與黑色幽默,探討《三便士歌劇》在當代劇場中的新詮釋,並藉由舞臺、燈光、服裝、音樂等多方面,分析該作如何在保留批判核心的同時,觸及觀眾的觀看位置與人性幽微。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
這是一場修復文化與重建精神的儀式,觀眾不需要完全看懂《遊林驚夢:巧遇Hagay》,但你能感受心與土地團聚的渴望,也不急著在此處釐清或定義什麼,但你的在場感受,就是一條線索,關於如何找著自己的路徑、自己的聲音。
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有說明如何使用uvicorn來啟動FastAPI服務, 假設今天我們的API是一個CPU密集型的運算服務時, 通常我們會希望開啟多個行程來幫忙處理, 那麼大致上的撰寫方式會像這樣: app = FastAPI( ti
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」有分享 FastAPI 這套API框架, 那麼當我們想要在應用程式剛執行時就註冊一些事件或者共享GPU運算模型、變數…等,當整個應用程式關閉時也進行釋放作業, 這樣的一個週期循環就是所謂的生命週期, 而在FastAPI這
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
5 月將於臺北表演藝術中心映演的「2026 北藝嚴選」《海妲・蓋柏樂》,由臺灣劇團「晃晃跨幅町」製作,本文將以從舞台符號、聲音與表演調度切入,討論海妲・蓋柏樂在父權社會結構下的困境,並結合榮格心理學與馮.法蘭茲對「阿尼姆斯」與「永恆少年」原型的分析,理解女人何以走向精神性的操控、毀滅與死亡。
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但開發出來之後, 我們會希望開放給一般使用者使用, 而一般使用者較能夠操作的媒介
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但開發出來之後, 我們會希望開放給一般使用者使用, 而一般使用者較能夠操作的媒介
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
當我們在開發一個AI應用服務時, 常常會需要載入大模型, But… 我們總不可能每一次的請求就載入一次模型吧! 這樣太沒有效率了, 也非常的浪費資源, 因此我們通常會希望應用程式啟動時就能夠載入模型, 之後每一次的請求只要讓模型進行運算即可, 那麼在FastAPI的框架中究竟要如何使用呢? 首
Thumbnail
API是我們與其他系統介接的標準化規格, 那一份好的規格勢必要能夠達到引導與驗證的作用, 避免對方介接錯誤, 引發後續的災難性損失, 因此這一章節就是要教我們如何定義每個API的欄位怎麼填? 資料型態是什麼? 以及如何生成API文件。 我們在「【🔒 Python API框架篇 - Fas
Thumbnail
API是我們與其他系統介接的標準化規格, 那一份好的規格勢必要能夠達到引導與驗證的作用, 避免對方介接錯誤, 引發後續的災難性損失, 因此這一章節就是要教我們如何定義每個API的欄位怎麼填? 資料型態是什麼? 以及如何生成API文件。 我們在「【🔒 Python API框架篇 - Fas
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但我們除了能開發出應用之外, 也要設計的更人性化一點, API最重要的就是路由了
Thumbnail
我們在「【🔒 Python API框架篇 - FastAPI】Ep.1 啟航」稍微帶大家認識了FastAPI這個框架, 它讓我們快速的架設一個API服務, 並提供了許多標準化功能, 只要照著規範走就能快速的開發出來, 但我們除了能開發出應用之外, 也要設計的更人性化一點, API最重要的就是路由了
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
要如何使用unicorn啟動多個FastAPI服務, 歡迎參考我們的「【💊 Python的解憂錦囊 - FastAPI】如何啟動多個Workers」。 當我們試著設計帶入模組化時… 我們在「【💊 Python的解憂錦囊 - FastAPI】使用 lifespan 來共享資料與管理生命週期
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
背景:從冷門配角到市場主線,算力與電力被重新定價   小P從2008進入股市,每一個時期的投資亮點都不同,記得2009蘋果手機剛上市,當時蘋果只要在媒體上提到哪一間供應鏈,隔天股價就有驚人的表現,當時光學鏡頭非常熱門,因為手機第一次搭上鏡頭可以拍照,也造就傳統相機廠的殞落,如今手機已經全面普及,題
Thumbnail
開發生涯,不斷堆加著程式碼,總有那麼一天,只是不經意地修改,卻...突然壞掉了!然後,修很久都修不好。這說明了一件重要的事:專案結構脆弱;隨著認知程度和專案規模的變大,標準也跟著一直提升。而這就是系統要「轉骨」的時候了,為了長治久安,必須進行重構,才能迎接下一階段的成長。以下紀錄我目前的最佳實行。
Thumbnail
開發生涯,不斷堆加著程式碼,總有那麼一天,只是不經意地修改,卻...突然壞掉了!然後,修很久都修不好。這說明了一件重要的事:專案結構脆弱;隨著認知程度和專案規模的變大,標準也跟著一直提升。而這就是系統要「轉骨」的時候了,為了長治久安,必須進行重構,才能迎接下一階段的成長。以下紀錄我目前的最佳實行。
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News