Files
MusicServer/app/http/stream_sign.py
T
snowy-yang 0df18f048b fix(server): 修复路由遮蔽、流式背压与鉴权限流顺序
- 专辑搜索静态路由优先注册,避免被 /album/{source}/{id}/{page} 捕获
- mg 流改为异步背压队列,慢客户端不再 QueueFull 悬挂,错误中断响应
- 限流移到鉴权外层,401 爆破计入限流
- 酷我 token 重试移出锁,修复 asyncio.Lock 递归死锁
- 补 CORS、5sing 音质、parse _kind、wy 首次登录 cookie、stream 签名溢出
- mg 流直连、lifespan 关闭 aiohttp 会话、cookie 登录路由与 auth_store 健壮性
- 测试改用 aiohttp + 进程内 uvicorn,移除 httpx/TestClient
- 更新 start.bat 与文档
2026-10-23 23:40:55 +08:00

39 lines
1.4 KiB
Python

"""流代理签名 —— /stream/* 的无头客户端兼容方案。
/url 返回流地址时附加 `?exp=<秒级时间戳>&sig=<HMAC-SHA256(authKey, "path:exp") 前 32 位>`。
与裸 ?key= 的区别:签名值泄露只能在该时效窗口内拉这一条流,不能反推出秘钥、不能调用其它接口。
"""
from __future__ import annotations
import hashlib
import hmac
import time
# 流地址有效期(秒),与 mg 流票据 TTL(10 分钟)对齐;过期后客户端重新 /url 即可
STREAM_SIGN_TTL = 10 * 60
def _sign(auth_key: str, data: str) -> str:
return hmac.new(auth_key.encode(), data.encode(), hashlib.sha256).hexdigest()[:32]
def sign_stream(auth_key: str, path: str) -> str:
"""生成流地址的签名参数(path 为不含 query 的路径,如 /stream/mg/123)。"""
exp = int(time.time() + STREAM_SIGN_TTL)
return f"exp={exp}&sig={_sign(auth_key, f'{path}:{exp}')}"
def verify_stream_sign(auth_key: str, path: str, exp: object, sig: object) -> bool:
"""校验流地址签名(常量时间比较 + 时效窗口)。"""
try:
exp_num = int(float(str(exp)))
except (TypeError, ValueError, OverflowError):
return False
sig_str = str(sig or "")
if not sig_str:
return False
if time.time() > exp_num: # 过期即拒(客户端重新 /url 取新地址)
return False
return hmac.compare_digest(_sign(auth_key, f"{path}:{exp_num}"), sig_str)