77 lines
2.1 KiB
Python
77 lines
2.1 KiB
Python
"""密码哈希 + JWT 签发/校验。
|
|
|
|
不用 passlib(新版 bcrypt 4.x 会让 passlib 的 wrap-bug 探测崩溃),直接用 bcrypt。
|
|
对超过 72 字节的密码先做 SHA-256 + base64 预处理,规避 bcrypt 上限。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import hashlib
|
|
from datetime import datetime, timedelta, timezone
|
|
from typing import Any
|
|
|
|
import bcrypt
|
|
from jose import JWTError, jwt
|
|
|
|
from app.core.config import settings
|
|
|
|
|
|
def _prepare(password: str) -> bytes:
|
|
raw = password.encode("utf-8")
|
|
if len(raw) > 72:
|
|
# SHA-256 固定 32 字节 → base64 后 44 字节,始终 < 72
|
|
raw = base64.b64encode(hashlib.sha256(raw).digest())
|
|
return raw
|
|
|
|
|
|
def hash_password(plain: str) -> str:
|
|
return bcrypt.hashpw(_prepare(plain), bcrypt.gensalt()).decode("ascii")
|
|
|
|
|
|
def verify_password(plain: str, hashed: str) -> bool:
|
|
try:
|
|
return bcrypt.checkpw(_prepare(plain), hashed.encode("ascii"))
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def _now() -> datetime:
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
def create_access_token(subject: str | int, extra: dict[str, Any] | None = None) -> str:
|
|
return _create_token(subject, settings.JWT_ACCESS_TTL, "access", extra)
|
|
|
|
|
|
def create_refresh_token(subject: str | int, extra: dict[str, Any] | None = None) -> str:
|
|
return _create_token(subject, settings.JWT_REFRESH_TTL, "refresh", extra)
|
|
|
|
|
|
def _create_token(
|
|
subject: str | int, ttl_seconds: int, token_type: str, extra: dict[str, Any] | None
|
|
) -> str:
|
|
payload: dict[str, Any] = {
|
|
"sub": str(subject),
|
|
"type": token_type,
|
|
"iat": _now(),
|
|
"exp": _now() + timedelta(seconds=ttl_seconds),
|
|
}
|
|
if extra:
|
|
payload.update(extra)
|
|
return jwt.encode(payload, settings.APP_SECRET_KEY, algorithm=settings.JWT_ALG)
|
|
|
|
|
|
def decode_token(token: str) -> dict[str, Any]:
|
|
"""解码 JWT,失败抛 `JWTError`。"""
|
|
return jwt.decode(token, settings.APP_SECRET_KEY, algorithms=[settings.JWT_ALG])
|
|
|
|
|
|
__all__ = [
|
|
"hash_password",
|
|
"verify_password",
|
|
"create_access_token",
|
|
"create_refresh_token",
|
|
"decode_token",
|
|
"JWTError",
|
|
]
|