0038_session_jti.py 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. """sys_user 加 session_jti 列(一次一码防重放,安全需求)。
  2. Revision ID: 0038_session_jti
  3. Revises: 0037_drop_audit_target_check
  4. Create Date: 2026-09-17
  5. 变更要点:
  6. 1. sys_user 加 session_jti:当前有效会话的授权码代(JWT jti)——写操作成功后
  7. 轮换出新码并在响应头返回,旧码写操作即拒(SESSION_ROTATED);
  8. 2. 读操作不比对(REST 读重放无危害,轮询与写并发时在途 GET 不掉线);
  9. 3. 兼容:NULL = 未纳管(存量会话 / 轮换开关关闭期间登录),校验跳过;
  10. 4. 双引擎幂等:先探测列存在再加(MySQL 与达梦升级互不冲突)。
  11. """
  12. from __future__ import annotations
  13. from collections.abc import Sequence
  14. import sqlalchemy as sa
  15. from alembic import op
  16. revision: str = "0038_session_jti"
  17. down_revision: str | None = "0037_drop_audit_target_check"
  18. branch_labels: str | Sequence[str] | None = None
  19. depends_on: str | Sequence[str] | None = None
  20. def _column_exists(table: str, column: str) -> bool:
  21. # 达梦 inspector 返回大写名,统一小写比较
  22. inspector = sa.inspect(op.get_bind())
  23. return column.lower() in {
  24. name.lower() for name in inspector.get_columns(table)
  25. }
  26. def upgrade() -> None:
  27. if _column_exists("sys_user", "session_jti"):
  28. return
  29. op.add_column("sys_user", sa.Column("session_jti", sa.String(length=64), nullable=True))
  30. def downgrade() -> None:
  31. if not _column_exists("sys_user", "session_jti"):
  32. return
  33. op.drop_column("sys_user", "session_jti")