""" 登录鉴权工具(JWT + 密码哈希) 设计目标: - 最小依赖:密码哈希用 pbkdf2_hmac(标准库) - JWT 使用 python-jose(已加入 requirements) """ from __future__ import annotations import base64 import hashlib import hmac import os import time from typing import Any, Dict, Optional from jose import jwt # type: ignore def _jwt_secret() -> str: s = (os.getenv("ATS_JWT_SECRET") or os.getenv("JWT_SECRET") or "").strip() if s: return s # 允许开发环境兜底,但线上务必配置 return "dev-secret-change-me" def jwt_encode(payload: Dict[str, Any], exp_sec: int = 3600) -> str: now = int(time.time()) body = dict(payload or {}) body["iat"] = now body["exp"] = now + int(exp_sec) return jwt.encode(body, _jwt_secret(), algorithm="HS256") def jwt_decode(token: str) -> Dict[str, Any]: return jwt.decode(token, _jwt_secret(), algorithms=["HS256"]) def _b64(b: bytes) -> str: return base64.urlsafe_b64encode(b).decode("utf-8").rstrip("=") def _b64d(s: str) -> bytes: s = (s or "").strip() s = s + ("=" * (-len(s) % 4)) return base64.urlsafe_b64decode(s.encode("utf-8")) def hash_password(password: str, iterations: int = 260_000) -> str: """ PBKDF2-SHA256:返回格式 pbkdf2_sha256$$$ """ pw = (password or "").encode("utf-8") salt = os.urandom(16) dk = hashlib.pbkdf2_hmac("sha256", pw, salt, int(iterations)) return f"pbkdf2_sha256${int(iterations)}${_b64(salt)}${_b64(dk)}" def verify_password(password: str, password_hash: str) -> bool: try: s = str(password_hash or "") if not s.startswith("pbkdf2_sha256$"): return False _, it_s, salt_b64, dk_b64 = s.split("$", 3) it = int(it_s) salt = _b64d(salt_b64) dk0 = _b64d(dk_b64) dk1 = hashlib.pbkdf2_hmac("sha256", (password or "").encode("utf-8"), salt, it) return hmac.compare_digest(dk0, dk1) except Exception: return False