Files
2026-10-15 20:36:59 +08:00

62 lines
2.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""安全组件:bcrypt 密码哈希 + JWT 双 Token(对齐《02-技术文档》4.2)"""
import uuid
from datetime import datetime, timedelta, timezone
import bcrypt
import jwt
from app.config import settings
from app.core import errors
from app.core.response import BizException
def hash_password(plain: str) -> str:
return bcrypt.hashpw(plain.encode(), bcrypt.gensalt()).decode()
def verify_password(plain: str, hashed: str) -> bool:
try:
return bcrypt.checkpw(plain.encode(), hashed.encode())
except ValueError:
return False
def _create_token(subject: int, token_type: str, ttl: int, epoch: int = 0) -> tuple[str, str, int]:
"""生成 JWT,返回 (token, jti, 过期时间戳)"""
now = datetime.now(timezone.utc)
jti = uuid.uuid4().hex
exp = now + timedelta(seconds=ttl)
payload = {
"sub": str(subject),
"jti": jti,
"type": token_type, # access / refresh / admin:{role}
"ep": epoch, # 令牌纪元:改密/注销后递增使旧 Token 全端失效
"iat": int(now.timestamp()),
"exp": exp,
}
token = jwt.encode(payload, settings.jwt_secret, algorithm=settings.jwt_algorithm)
return token, jti, int(exp.timestamp())
def create_access_token(user_id: int, epoch: int = 0) -> tuple[str, str, int]:
return _create_token(user_id, "access", settings.access_token_ttl, epoch)
def create_refresh_token(user_id: int, epoch: int = 0) -> tuple[str, str, int]:
return _create_token(user_id, "refresh", settings.refresh_token_ttl, epoch)
def create_admin_token(admin_id: int, role: str) -> tuple[str, str, int]:
"""管理员 Token,payload 带角色,有效期 12h"""
token, jti, exp = _create_token(admin_id, f"admin:{role}", settings.admin_token_ttl)
return token, jti, exp
def decode_token(token: str) -> dict:
"""解码并校验 Token,失败抛 1002"""
try:
payload = jwt.decode(token, settings.jwt_secret, algorithms=[settings.jwt_algorithm])
except jwt.PyJWTError:
raise BizException(errors.UNAUTHORIZED, http_status=401)
return payload