62 lines
1.8 KiB
Python
62 lines
1.8 KiB
Python
|
|
"""认证:密码哈希、令牌签发/校验、短信验证码(兼容原 mock 方案)"""
|
|||
|
|
import base64
|
|||
|
|
import hashlib
|
|||
|
|
import json
|
|||
|
|
import time
|
|||
|
|
import random
|
|||
|
|
import re
|
|||
|
|
|
|||
|
|
SECRET = "pine-mock-secret-2026"
|
|||
|
|
TOKEN_TTL_MS = 1000 * 60 * 60 * 12 # 12h
|
|||
|
|
PHONE_RE = re.compile(r"^1\d{10}$")
|
|||
|
|
CODE_TTL = 5 * 60 # 5 min
|
|||
|
|
|
|||
|
|
# 演示环境:内存验证码 map(phone -> {code, exp}),响应返回 debugCode
|
|||
|
|
sms_codes: dict[str, dict] = {}
|
|||
|
|
|
|||
|
|
|
|||
|
|
def hash_password(s: str) -> str:
|
|||
|
|
return hashlib.sha256(str(s).encode()).hexdigest()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def b64url(data: bytes) -> str:
|
|||
|
|
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def make_token(username: str) -> str:
|
|||
|
|
payload = b64url(json.dumps({"username": username, "exp": int(time.time() * 1000) + TOKEN_TTL_MS}).encode())
|
|||
|
|
sig = b64url((SECRET + "." + payload).encode())
|
|||
|
|
return f"{payload}.{sig}"
|
|||
|
|
|
|||
|
|
|
|||
|
|
def verify_token(token: str):
|
|||
|
|
if not token or not isinstance(token, str):
|
|||
|
|
return None
|
|||
|
|
parts = token.split(".")
|
|||
|
|
if len(parts) != 2:
|
|||
|
|
return None
|
|||
|
|
payload_part, sig_part = parts
|
|||
|
|
if b64url((SECRET + "." + payload_part).encode()) != sig_part:
|
|||
|
|
return None
|
|||
|
|
try:
|
|||
|
|
payload = json.loads(base64.urlsafe_b64decode(payload_part + "=" * (-len(payload_part) % 4)).decode())
|
|||
|
|
if not payload.get("exp") or payload["exp"] < time.time() * 1000:
|
|||
|
|
return None
|
|||
|
|
return {"username": payload["username"]}
|
|||
|
|
except Exception:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
|
|||
|
|
def send_sms_code(phone: str) -> str:
|
|||
|
|
code = str(random.randint(100000, 999999))
|
|||
|
|
sms_codes[phone] = {"code": code, "exp": time.time() + CODE_TTL}
|
|||
|
|
return code
|
|||
|
|
|
|||
|
|
|
|||
|
|
def check_sms_code(phone: str, code: str) -> bool:
|
|||
|
|
rec = sms_codes.get(phone)
|
|||
|
|
if not rec or rec["code"] != str(code) or time.time() > rec["exp"]:
|
|||
|
|
return False
|
|||
|
|
sms_codes.pop(phone, None)
|
|||
|
|
return True
|