4723554e29
- 替换 passlib.context.CryptContext 为直接 bcrypt 调用 - hash_password: bcrypt.hashpw + gensalt - verify_password: bcrypt.checkpw + 异常处理 - 保持公共接口不变(hash_password/verify_password/create_token/verify_token) - 移除 passlib 导入,直接使用 bcrypt 模块 - 现有 $2b$12$... bcrypt hash 仍可验证通过 修复问题: - passlib 1.7.4 探测 bcrypt.__about__.__version__ 导致 AttributeError - 现代 bcrypt 移除了 __about__ 属性 - 致使容器内密码验证失败,登录跳过 验证结果: - 本地 test_auth.py: 3/3 PASS - 容器 test_auth.py: 3/3 PASS - 容器 smoke JWT LOGIN: PASS(之前 SKIP) Co-Authored-By: Claude <noreply@anthropic.com>
41 lines
1.3 KiB
Python
41 lines
1.3 KiB
Python
# sanguo_api/auth.py
|
|
"""JWT 单用户认证。secret/用户名/密码 hash 来自 config/backtest.yaml。"""
|
|
import os
|
|
from datetime import datetime, timedelta, timezone
|
|
import jwt
|
|
import bcrypt
|
|
from fastapi import HTTPException, status
|
|
|
|
_CONFIG = {"secret": "change-me", "expire_minutes": 60, "algorithm": "HS256"}
|
|
|
|
|
|
def set_jwt_config(secret: str, expire_minutes: int, algorithm: str = "HS256"):
|
|
_CONFIG.update(secret=secret, expire_minutes=expire_minutes, algorithm=algorithm)
|
|
|
|
|
|
def hash_password(password: str) -> str:
|
|
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
|
|
|
|
|
def verify_password(password: str, password_hash: str) -> bool:
|
|
try:
|
|
return bcrypt.checkpw(password.encode("utf-8"), password_hash.encode("utf-8"))
|
|
except (ValueError, TypeError):
|
|
return False
|
|
|
|
|
|
def create_token(username: str) -> str:
|
|
payload = {
|
|
"sub": username,
|
|
"exp": datetime.now(timezone.utc) + timedelta(minutes=_CONFIG["expire_minutes"]),
|
|
}
|
|
return jwt.encode(payload, _CONFIG["secret"], algorithm=_CONFIG["algorithm"])
|
|
|
|
|
|
def verify_token(token: str) -> str:
|
|
try:
|
|
payload = jwt.decode(token, _CONFIG["secret"], algorithms=[_CONFIG["algorithm"]])
|
|
return payload["sub"]
|
|
except jwt.PyJWTError:
|
|
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效 token")
|