""" JWT 签发/校验、密码哈希。 统一使用 PyJWT(不再使用 python-jose)。 """ import secrets from datetime import datetime, timedelta, timezone from typing import Dict, Optional import bcrypt import jwt from app.core.config import settings ALGORITHM = settings.algorithm SECRET_KEY = settings.secret_key ACCESS_TOKEN_EXPIRE_MINUTES = settings.access_token_expire_minutes REFRESH_TOKEN_EXPIRE_DAYS = settings.refresh_token_expire_days def hash_password(password: str) -> str: password_bytes = password.encode("utf-8") salt = bcrypt.gensalt() hashed = bcrypt.hashpw(password_bytes, salt) return hashed.decode("utf-8") def verify_password(plain_password: str, hashed_password: str) -> bool: try: return bcrypt.checkpw( plain_password.encode("utf-8"), hashed_password.encode("utf-8"), ) except Exception: return False def create_access_token(data: Dict, expires_delta: Optional[timedelta] = None) -> str: to_encode = data.copy() expire = datetime.now(timezone.utc) + ( expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) ) to_encode.update({"exp": expire, "type": "access"}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) def create_refresh_token() -> str: return secrets.token_urlsafe(32) def verify_token(token: str) -> Optional[Dict]: try: return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) except jwt.InvalidTokenError: return None def get_token_expires_at() -> datetime: return datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)