Archived
为什么:单文件 1100~1900 行的巨石模块让「改一处要通读全文」;同时注释里残留 大量已删台账的编号,以及「Flask/FastAPI 双栈迁移期」这类已失实的架构描述, 会误导后来者按不复存在的架构去改代码。 拆分(对外导入路径不变,行为零变更): - music_downloader.py -> music_downloader/(models/downloader/cover/_http/_log/_concurrency) - auth_system.py -> auth_system/(db/security/users/credits/ledger/mail/cards/backup...) - music_api.py -> music_api/(api/client/crypto/qr/cookies/compat/constants/errors) - services/runtime.py -> services/(api_service/_shared/_taskcore/downloads/batch/podcast/ streaming/tasks);services/runtime.py 保留为兼容门面 兼容性做法:各包用 __init__.py 以 __all__ 原样再导出全部符号,既有调用点 (auth_system.xxx / services.xxx / from music_api import Y)零改动;路径常量改为经包属性 动态读取(auth_system.DB_PATH),否则测试的 monkeypatch 会静默失效并写到生产库。 修复 9 处代码损伤(上一轮注释清理把 `()` 当「空括号残留」删掉所致;用「与拆分前逐字 等价」校验逐一定位,静态检查只会报出其中 4 处): - core/config.py ).strip() or '*' - core/state.py threading.Lock() x2 - qr_login.py datetime.now() / .strip() - music_downloader/downloader.py .lower() - services/podcast.py set() / time.time() x2 清理与删除: - 删除 部署文档.md / 功能介绍.md / README_私有包警告.txt - 清理指向已删台账与文档的过时编号注释;把「迁移期 / 两栈 / 双框架 / 已迁移」等 失实描述改写为现状描述(保留 `<-> Flask /path` 映射表等仍有价值的契约来源说明) - _mkpkg.py:公开包剔除清单去掉已不存在的文档名;私有包警告改为自包含 - pyproject.toml:补 pythonpath = ["."],使裸跑 pytest 不再 import core 失败 验证:ruff 全通过;pyright 0 errors / 0 warnings(102 文件);pytest 144 passed; 真实 uvicorn 冒烟 6 条路由符合预期且生产 data/ 指纹未变;忽略注释的逐字等价性校验通过。
57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""口令哈希常量与校验。
|
||
|
||
(自 auth_system.py 拆出,行为与原实现一致。)
|
||
"""
|
||
|
||
import hashlib
|
||
import secrets
|
||
|
||
# 口令哈希升级。旧库 SHA-256(salt+pwd) 哈希在登录成功时透明升级为 PBKDF2
|
||
_PBKDF2_ITERS = 100_000
|
||
|
||
|
||
# ============ 密码与用户 ============
|
||
|
||
|
||
def _hash(password: str, salt: str) -> str:
|
||
"""旧版口令哈希:SHA-256(salt + password)。仅用于校验存量库,新哈希一律走 _hash_new"""
|
||
return hashlib.sha256((salt + password).encode('utf-8')).hexdigest()
|
||
|
||
|
||
def _hash_new(password: str, salt: str = '', iterations: int = _PBKDF2_ITERS) -> str:
|
||
"""新版口令哈希:PBKDF2-HMAC-SHA256,格式 pbkdf2$iter$salt$digest"""
|
||
salt = salt or secrets.token_hex(8)
|
||
digest = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'),
|
||
salt.encode('utf-8'), iterations).hex()
|
||
return f'pbkdf2${iterations}${salt}${digest}'
|
||
|
||
|
||
def _verify_password(stored: str, password: str, salt: str) -> tuple[bool, bool]:
|
||
"""校验口令。返回 (是否正确, 是否需要升级到 PBKDF2)"""
|
||
if stored.startswith('pbkdf2$'):
|
||
try:
|
||
_, iters, psalt, digest = stored.split('$', 3)
|
||
calc = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'),
|
||
psalt.encode('utf-8'), int(iters)).hex()
|
||
return secrets.compare_digest(calc, digest), False
|
||
except Exception:
|
||
return False, False
|
||
ok = secrets.compare_digest(stored, _hash(password, salt))
|
||
return ok, ok # 旧哈希校验通过 → 登录时升级
|
||
|
||
|
||
def _normalize_email(email: str) -> str:
|
||
""":邮箱归一化——剥离 Gmail 等服务商的 +tag 别名
|
||
(user+1@ 与 user@ 视为同一邮箱),堵住别名的无限刷号绕过;无 + 原样返回。
|
||
local 为空(如 '+a@gmail.com')时返回空串,由调用方按格式错误拒绝。"""
|
||
email = (email or '').strip().lower()
|
||
if '+' in email:
|
||
local, _, domain = email.partition('@')
|
||
local = local.split('+', 1)[0]
|
||
if local and domain:
|
||
email = f'{local}@{domain}'
|
||
elif not local:
|
||
return ''
|
||
return email
|