auth.py 1.7 KB

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