"""面板鉴权:itsdangerous 签名 Cookie。 密码来源优先级:环境变量 LARK_PANEL_PASSWORD > config.panel.password。 未设置密码时面板无法登录(拒绝空密码)。 """ from __future__ import annotations import os import time from typing import Optional from itsdangerous import BadSignature, SignatureExpired, URLSafeTimedSerializer COOKIE_NAME = "lark2lark_session" COOKIE_MAX_AGE = 7 * 24 * 3600 # 7 天 def _get_secret_key(panel_password: str) -> str: """用面板密码派生签名密钥(固定 salt 保证进程间一致)。""" # 密码本身就是准入凭据,直接作为签名密钥源;加固定前缀防止空密码 return f"lark2lark::{panel_password}" def has_password(panel_password: str) -> bool: return bool(panel_password and panel_password.strip()) def sign_session(panel_password: str) -> str: """登录成功后签发会话 token。""" serializer = URLSafeTimedSerializer(_get_secret_key(panel_password), salt="session") return serializer.dumps({"login_at": int(time.time())}) def verify_session(token: str, panel_password: str) -> bool: """校验会话 token。失败返回 False。""" if not token: return False serializer = URLSafeTimedSerializer(_get_secret_key(panel_password), salt="session") try: serializer.loads(token, max_age=COOKIE_MAX_AGE) return True except (BadSignature, SignatureExpired): return False def verify_password(input_password: str, expected_password: str) -> bool: """恒定时间比较,防侧信道计时攻击。""" if not expected_password: return False import hmac return hmac.compare_digest(input_password or "", expected_password)