jwt_tokens.py 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. """JWT 签发与严格校验。"""
  2. from __future__ import annotations
  3. from datetime import datetime, timedelta, timezone
  4. from uuid import uuid4
  5. import jwt
  6. from flask import current_app
  7. from dms.common.errors import AuthenticationError, InternalError, TokenExpiredError
  8. from dms.models import User
  9. ALGORITHM = "HS256"
  10. REQUIRED_CLAIMS = ("sub", "username", "roleCode", "authVersion", "iat", "exp", "jti", "iss")
  11. def _secret() -> str:
  12. secret = str(current_app.config.get("DMS_JWT_SECRET", "")).strip()
  13. if not secret:
  14. raise InternalError("JWT签名密钥未配置")
  15. return secret
  16. def issue_access_token(user: User, *, keep_signed_in: bool) -> tuple[str, int]:
  17. now = datetime.now(timezone.utc)
  18. duration_key = (
  19. "DMS_JWT_KEEP_SIGNED_IN_EXPIRES_SECONDS"
  20. if keep_signed_in
  21. else "DMS_JWT_EXPIRES_SECONDS"
  22. )
  23. expires_in = int(current_app.config[duration_key])
  24. claims = {
  25. "sub": str(user.id),
  26. "username": user.username,
  27. "roleCode": user.role_code,
  28. "authVersion": user.auth_version,
  29. "iat": now,
  30. "exp": now + timedelta(seconds=expires_in),
  31. "jti": str(uuid4()),
  32. "iss": current_app.config["DMS_JWT_ISSUER"],
  33. }
  34. return jwt.encode(claims, _secret(), algorithm=ALGORITHM), expires_in
  35. def decode_access_token(token: str) -> dict[str, object]:
  36. try:
  37. return jwt.decode(
  38. token,
  39. _secret(),
  40. algorithms=[ALGORITHM],
  41. issuer=current_app.config["DMS_JWT_ISSUER"],
  42. options={"require": list(REQUIRED_CLAIMS)},
  43. )
  44. except jwt.ExpiredSignatureError as exc:
  45. raise TokenExpiredError() from exc
  46. except jwt.PyJWTError as exc:
  47. raise AuthenticationError() from exc