76 lines
1.8 KiB
Python
76 lines
1.8 KiB
Python
"""管理员认证模块
|
|
|
|
密码存储: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)
|