"""密码哈希与 Token 工具。 密码:Argon2id(argon2-cffi) Token:secrets.token_urlsafe + SHA-256 hash + Fernet 加密 """ import hashlib import secrets from argon2 import PasswordHasher from argon2.exceptions import VerifyMismatchError from cryptography.fernet import Fernet from app.core.config import get_settings _ph = PasswordHasher() def hash_password(password: str) -> str: """Argon2id 哈希密码。""" return _ph.hash(password) def verify_password(password: str, password_hash: str) -> bool: """验证密码。""" try: return _ph.verify(password_hash, password) except VerifyMismatchError: return False def generate_token() -> str: """生成 128bit 熵 URL-safe token(22 字符)。""" return secrets.token_urlsafe(16) def hash_token(token: str) -> str: """SHA-256 哈希 token(用于数据库存储和查询)。""" return hashlib.sha256(token.encode()).hexdigest() def get_fernet() -> Fernet: """从 SECRET_KEY 派生 Fernet 实例(用于加密存储 token 原文)。""" settings = get_settings() # 将 SECRET_KEY 转为 32 字节 Fernet key(简单派生,MVP 够用) key = hashlib.sha256(settings.secret_key.encode()).digest() import base64 return Fernet(base64.urlsafe_b64encode(key)) def encrypt_token(token: str) -> str: """Fernet 加密 token 原文。""" return get_fernet().encrypt(token.encode()).decode() def decrypt_token(encrypted: str) -> str: """Fernet 解密 token 原文。""" return get_fernet().decrypt(encrypted.encode()).decode()