什麼是 OAuth2 與 Password Flow?
OAuth2 是一種標準授權框架,而 Password Flow 是其中一種允許使用者直接使用帳號密碼換取存取權杖 (Access Token) 的模式。
Password Flow 基礎應用
OAuth2PasswordBearer(tokenUrl="token"):實例化安全依賴項目,tokenUrl 參數告訴前端登入端點的位置Annotated[OAuth2PasswordRequestForm, Depends()]: 在登入路由中注入此依賴,專門解析符合 OAuth2 規範的表單資料。- Token 回傳格式:登入成功後,必須回傳包含
access_token(字串) 與token_type(通常為 "bearer") 的 JSON 物件 -
Annotated[str, Depends(oauth2_scheme)]:在受保護的路由中使用此依賴,FastAPI 會自動從 Header 提取 Authorization 欄位並驗證
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from typing import Annotated
app = FastAPI()
# 1. 定義 Pydantic 模型
class Token(BaseModel):
access_token: str
token_type: str
class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None
# 2. 模擬使用者資料庫
users_db = {
"admin": {
"username": "admin",
"full_name": "Admin User",
"email": "[email protected]",
"hashed_password": "hashed_secret_123", # 模擬雜湊後的密碼
"disabled": False,
},
"test_user": {
"username": "test_user",
"full_name": "Test User",
"email": "[email protected]",
"hashed_password": "hashed_password_abc",
"disabled": False,
}
}
# 3. 初始化 OAuth2 依賴
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 模擬密碼驗證
def verify_password(plain_password: str, hashed_password: str) -> bool:
if plain_password == "secret":
return True
return False
# 根據 Token 獲取使用者
def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
# 這裡簡化邏輯:直接假設 token 等於使用者名稱
user_dict = users_db.get(token)
if not user_dict:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="無法驗證憑證 (Could not validate credentials)",
headers={"WWW-Authenticate": "Bearer"},
)
# 將字典轉換為 Pydantic 模型回傳,提供型別提示
return User(**user_dict)
# 4. 登入路由 (Token Endpoint)
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
# 檢查使用者是否存在
user_dict = users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="帳號或密碼錯誤")
# 驗證密碼
# 這裡刻意簡化,只要輸入 "secret" 就會通過
if not verify_password(form_data.password, user_dict["hashed_password"]):
raise HTTPException(status_code=400, detail="帳號或密碼錯誤")
# 登入成功,回傳 Token
# access_token 這裡暫時使用 username 代替
return Token(access_token=user_dict["username"], token_type="bearer")
# 5. 受保護路由
@app.get("/users/me", response_model=User)
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user



















