| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 |
- """登录失败锁定:sys_user 增加连续失败计数与锁定截止时间列。
- Revision ID: 0022_login_lockout
- Revises: 0021_remove_assigned_scope
- Create Date: 2026-09-01
- 变更要点:
- 1. sys_user 增加 failed_login_count INT NOT NULL DEFAULT 0(连续密码错误计数,
- 仅 /auth/login 密码错误计入,登录成功清零);
- 2. sys_user 增加 locked_until DATETIME NULL(锁定截止时间,UTC,NULL=未锁定;
- 连续5次密码错误锁定15分钟,由服务层 login_lockout 维护);
- 3. 升级幂等:列已存在时跳过;降级直接删列(锁定状态为易失运行态,可随时丢弃)。
- """
- from __future__ import annotations
- from collections.abc import Sequence
- import sqlalchemy as sa
- from alembic import op
- revision: str = "0022_login_lockout"
- down_revision: str | None = "0021_remove_assigned_scope"
- branch_labels: str | Sequence[str] | None = None
- depends_on: str | Sequence[str] | None = None
- _TABLE = "sys_user"
- _COLUMNS = ("failed_login_count", "locked_until")
- def _existing_columns() -> set[str]:
- inspector = sa.inspect(op.get_bind())
- return {column["name"] for column in inspector.get_columns(_TABLE)}
- def upgrade() -> None:
- existing = _existing_columns()
- if "failed_login_count" not in existing:
- op.add_column(
- _TABLE,
- sa.Column(
- "failed_login_count",
- sa.Integer(),
- nullable=False,
- server_default=sa.text("0"),
- ),
- )
- if "locked_until" not in existing:
- op.add_column(
- _TABLE,
- sa.Column("locked_until", sa.DateTime(), nullable=True),
- )
- def downgrade() -> None:
- existing = _existing_columns()
- if "locked_until" in existing:
- op.drop_column(_TABLE, "locked_until")
- if "failed_login_count" in existing:
- op.drop_column(_TABLE, "failed_login_count")
|