61 lines
2.1 KiB
Python
61 lines
2.1 KiB
Python
"""登录 / Token 刷新业务逻辑。"""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.core.exceptions import AppError
|
|
from app.core.security import (
|
|
JWTError,
|
|
create_access_token,
|
|
create_refresh_token,
|
|
decode_token,
|
|
verify_password,
|
|
)
|
|
from app.models import User
|
|
from app.schemas.auth import TokenPair
|
|
from app.core.config import settings
|
|
|
|
|
|
async def login(db: AsyncSession, username: str, password: str) -> tuple[User, TokenPair]:
|
|
stmt = select(User).where(User.username == username)
|
|
user = (await db.execute(stmt)).scalar_one_or_none()
|
|
if user is None or not verify_password(password, user.password_hash):
|
|
raise AppError("账号或密码错误", code=401, http_status=status.HTTP_401_UNAUTHORIZED)
|
|
if user.status != "ACTIVE":
|
|
raise AppError("账号已禁用", code=403, http_status=status.HTTP_403_FORBIDDEN)
|
|
|
|
access = create_access_token(user.id)
|
|
refresh = create_refresh_token(user.id)
|
|
return user, TokenPair(
|
|
access_token=access,
|
|
refresh_token=refresh,
|
|
expires_in=settings.JWT_ACCESS_TTL,
|
|
)
|
|
|
|
|
|
async def refresh(db: AsyncSession, refresh_token: str) -> TokenPair:
|
|
try:
|
|
payload = decode_token(refresh_token)
|
|
except JWTError as e:
|
|
raise AppError(
|
|
"refresh_token 无效", code=401, http_status=status.HTTP_401_UNAUTHORIZED
|
|
) from e
|
|
if payload.get("type") != "refresh":
|
|
raise AppError("token 类型错误", code=401, http_status=status.HTTP_401_UNAUTHORIZED)
|
|
try:
|
|
user_id = int(payload["sub"])
|
|
except (KeyError, ValueError) as e:
|
|
raise AppError("token 无效", code=401, http_status=status.HTTP_401_UNAUTHORIZED) from e
|
|
|
|
user = await db.get(User, user_id)
|
|
if user is None or user.status != "ACTIVE":
|
|
raise AppError("账号已失效", code=401, http_status=status.HTTP_401_UNAUTHORIZED)
|
|
|
|
return TokenPair(
|
|
access_token=create_access_token(user.id),
|
|
refresh_token=create_refresh_token(user.id),
|
|
expires_in=settings.JWT_ACCESS_TTL,
|
|
)
|