"""密码散列与 JWT 工具。""" from __future__ import annotations import hashlib import hmac import os import secrets from datetime import datetime, timedelta, timezone import jwt PBKDF2_ITERATIONS = 200_000 def hash_password(password: str) -> str: salt = secrets.token_hex(16) digest = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), bytes.fromhex(salt), PBKDF2_ITERATIONS ).hex() return f"pbkdf2_sha256${PBKDF2_ITERATIONS}${salt}${digest}" def verify_password(password: str, stored: str) -> bool: try: algorithm, iterations, salt, expected = stored.split("$") if algorithm != "pbkdf2_sha256": return False digest = hashlib.pbkdf2_hmac( "sha256", password.encode("utf-8"), bytes.fromhex(salt), int(iterations) ).hex() return hmac.compare_digest(digest, expected) except (ValueError, TypeError): return False def _jwt_secret() -> str: return os.getenv("JWT_SECRET", "nex-math-dev-secret-change-me-in-production") def create_access_token(user_id: int, expires_hours: int = 24 * 7) -> str: now = datetime.now(timezone.utc) payload = { "sub": str(user_id), "iat": now, "exp": now + timedelta(hours=expires_hours), } return jwt.encode(payload, _jwt_secret(), algorithm="HS256") def decode_access_token(token: str) -> int | None: try: payload = jwt.decode(token, _jwt_secret(), algorithms=["HS256"]) return int(payload["sub"]) except (jwt.PyJWTError, KeyError, ValueError): return None