68 lines
1.8 KiB
Python
68 lines
1.8 KiB
Python
"""
|
||
FastAPI 依赖注入:从 JWT 提取当前用户信息。
|
||
|
||
用法:
|
||
@router.get("/protected")
|
||
async def protected_endpoint(user: CurrentUser = Depends(get_current_user)):
|
||
print(user.user_id, user.site_id)
|
||
"""
|
||
|
||
from dataclasses import dataclass
|
||
|
||
from fastapi import Depends, HTTPException, status
|
||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||
from jose import JWTError
|
||
|
||
from app.auth.jwt import decode_access_token
|
||
|
||
# Bearer token 提取器
|
||
_bearer_scheme = HTTPBearer(auto_error=True)
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class CurrentUser:
|
||
"""从 JWT 解析出的当前用户上下文。"""
|
||
|
||
user_id: int
|
||
site_id: int
|
||
|
||
|
||
async def get_current_user(
|
||
credentials: HTTPAuthorizationCredentials = Depends(_bearer_scheme),
|
||
) -> CurrentUser:
|
||
"""
|
||
FastAPI 依赖:从 Authorization header 提取 JWT,验证后返回用户信息。
|
||
|
||
失败时抛出 401。
|
||
"""
|
||
token = credentials.credentials
|
||
try:
|
||
payload = decode_access_token(token)
|
||
except JWTError:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
detail="无效的令牌",
|
||
headers={"WWW-Authenticate": "Bearer"},
|
||
)
|
||
|
||
user_id_raw = payload.get("sub")
|
||
site_id = payload.get("site_id")
|
||
|
||
if user_id_raw is None or site_id is None:
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
detail="令牌缺少必要字段",
|
||
headers={"WWW-Authenticate": "Bearer"},
|
||
)
|
||
|
||
try:
|
||
user_id = int(user_id_raw)
|
||
except (TypeError, ValueError):
|
||
raise HTTPException(
|
||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||
detail="令牌中 user_id 格式无效",
|
||
headers={"WWW-Authenticate": "Bearer"},
|
||
)
|
||
|
||
return CurrentUser(user_id=user_id, site_id=site_id)
|