"""管理员认证模块 密码存储:data/admin.json - password_hash: sha256 哈希 - salt: 随机盐 """ import hashlib import json import os import secrets import time DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") ADMIN_FILE = os.path.join(DATA_DIR, "admin.json") # 登录 token 有效期 24 小时 TOKEN_TTL = 86400 # 内存中的活跃 token: {token: expire_ts} _active_tokens: dict[str, float] = {} def _hash_password(password: str, salt: str) -> str: return hashlib.sha256(f"{salt}:{password}".encode()).hexdigest() def is_configured() -> bool: return os.path.exists(ADMIN_FILE) def set_password(password: str) -> None: """设置或更新管理员密码""" os.makedirs(DATA_DIR, exist_ok=True) salt = secrets.token_hex(16) data = { "password_hash": _hash_password(password, salt), "salt": salt, "updated_at": int(time.time()), } with open(ADMIN_FILE, "w") as f: json.dump(data, f, indent=2) def verify_password(password: str) -> bool: """验证密码是否正确""" if not is_configured(): return False with open(ADMIN_FILE) as f: data = json.load(f) return _hash_password(password, data["salt"]) == data["password_hash"] def create_token() -> str: """创建登录 token""" token = secrets.token_urlsafe(32) _active_tokens[token] = time.time() + TOKEN_TTL return token def verify_token(token: str) -> bool: """验证 token 是否有效""" if not token: return False expire = _active_tokens.get(token) if expire is None: return False if time.time() > expire: del _active_tokens[token] return False return True def revoke_token(token: str) -> None: """吊销 token""" _active_tokens.pop(token, None)