980a2db6d9
- 基础设施层 async:SQLAlchemy 异步引擎/会话、33 Repository async 化、models/security/seed 迁入 infrastructure、新增 cache.py(redis.asyncio) 与 oss.py(aioboto3) - 接口层:routers 迁 api/routers 并全 async,dependencies 迁 api/dependencies(get_db/get_current_user async) - 依赖:sqlalchemy[asyncio]/aiosqlite/asyncmy/redis/aioboto3;config 异步 URL + Redis/OSS 配置 - 删除废弃:旧同步 db/dependencies/repositories/storage - 验证:平台 19 路由 + 培训 48 路由全注册;/health /auth/login /auth/me /admin/tasks /notifications 等接口 async 可用
32 lines
1.1 KiB
Python
32 lines
1.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""密码哈希与令牌生成(仅用标准库,无额外依赖)。
|
|
|
|
- 密码:加盐 SHA-256,与 PineAgents 主后端历史实现一致(salted SHA-256)。
|
|
- 令牌:不透明随机串,存于 tokens.json。下一阶段换成数据库时,
|
|
该随机串即 ``tokens.token`` 列的主键值,语义与 JWT/会话表一致。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import hmac
|
|
import secrets
|
|
|
|
|
|
def hash_password(password: str, salt: str | None = None) -> tuple[str, str]:
|
|
"""计算 (hash_hex, salt_hex)。未提供 salt 时自动生成随机盐。"""
|
|
if salt is None:
|
|
salt = secrets.token_hex(16)
|
|
digest = hashlib.sha256((salt + password).encode("utf-8")).hexdigest()
|
|
return digest, salt
|
|
|
|
|
|
def verify_password(password: str, stored_hash: str, salt: str) -> bool:
|
|
"""常数时间比较,防时序侧信道。"""
|
|
digest, _ = hash_password(password, salt)
|
|
return hmac.compare_digest(digest, stored_hash)
|
|
|
|
|
|
def generate_token() -> str:
|
|
"""生成不透明访问令牌(URL-safe,约 64 字符)。"""
|
|
return secrets.token_urlsafe(48)
|