MySQL 8.0中唯一可靠的密码过期控制方式是ALTER USER PASSWORD EXPIRE INTERVAL;不支持自动账号锁定,仅通过强制改密拦截实现策略,且必须用ALTER USER设置,不能用SET PASSWORD或直接修改mysql.user表。

ALTER USER PASSWORD EXPIRE INTERVAL 是唯一可靠方式
MySQL 8.0 不支持“自动锁定账号”或“登录失败 N 次后冻结”这类行为,所谓“锁定策略”在原生层面仅体现为密码过期后的强制改密拦截——用户仍能连上,但执行 SELECT 1 就报 ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement。
设置过期时间必须用 ALTER USER,不能靠 SET PASSWORD 或直接 UPDATE mysql.user 表:
-
ALTER USER 'app'@'10.%.%.%' PASSWORD EXPIRE INTERVAL 90 DAY:从password_last_changed时间起 90 天后触发强制改密 -
ALTER USER 'dba'@'%' PASSWORD EXPIRE:立即过期,下次登录即卡住 -
ALTER USER 'svc'@'localhost' PASSWORD EXPIRE NEVER:显式禁用过期(服务账号推荐) -
ALTER USER 'admin'@'%' PASSWORD EXPIRE DEFAULT:继承@@default_password_lifetime值,不是“不过期”
注意:user@host 必须完全匹配,'root'@'localhost' 和 'root'@'%' 是两个独立账户,漏掉任意一个都可能留下安全缺口。
default_password_lifetime 只影响新用户,老用户必须逐个覆盖
设 SET PERSIST default_password_lifetime = 90 或在 /etc/my.cnf 的 [mysqld] 段加 default_password_lifetime = 90,只对后续 CREATE USER 创建的账号生效。已存在的用户(包括刚重置过密码的 root)password_lifetime 字段仍是 NULL,查 mysql.user 看到一堆 NULL 是正常现象,不代表配置失败。
让老用户真正受控,只能手动执行 ALTER USER:
- 批量生成命令可查表:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' PASSWORD EXPIRE INTERVAL 90 DAY;') FROM mysql.user WHERE password_last_changed IS NOT NULL; - 执行前先确认
password_last_changed非空,否则过期计算会出错(旧密码时间戳未更新) - 若某用户是用
SET PASSWORD改的密,password_last_changed不会刷新,建议补一句ALTER USER 'u'@'h' IDENTIFIED BY 'xxx';
没有原生账号锁定,但可用 password_history + password_reuse_interval 防弱管控
MySQL 8.0.19+ 提供 password_history 和 password_reuse_interval,它们不等于“锁定”,但能阻止用户绕过过期策略——比如把 p@ss123 改成 p@ss124 再改回 p@ss123。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
这两个参数必须同时设置,且依赖 validate_password 组件启用才生效:
-
SET PERSIST password_history = 5;:记住最近 5 次密码,第 6 次不能重复使用其中任一 -
SET PERSIST password_reuse_interval = 365;:同一密码 365 天内不可重用 - 必须用
ALTER USER ... IDENTIFIED BY改密才会触发校验;SET PASSWORD已弃用,不校验历史 - 版本低于 8.0.19 时该功能不完整,执行
SELECT VERSION();先确认
查是否生效:SELECT @@global.password_history, @@global.password_reuse_interval; 返回非 NULL 值才算成功。
验证过期是否真起作用,别只看 password_expired 字段
password_expired = 'Y' 只表示被手动设为过期(如执行了 PASSWORD EXPIRE),不代表“已超期”。真正超期要靠 password_last_changed 和 default_password_lifetime 计算:
- 查显式过期用户:
SELECT user, host FROM mysql.user WHERE password_expired = 'Y'; - 查自动超期(含已过期 + 7 天内将过期):
SELECT user, host, password_last_changed, TIMESTAMPADD(DAY, @@default_password_lifetime, password_last_changed) AS expires_at FROM mysql.user WHERE password_last_changed IS NOT NULL AND @@default_password_lifetime > 0 AND TIMESTAMPADD(DAY, @@default_password_lifetime, password_last_changed) -
password_last_changed是 UTC 时间,和本地时区无关
最直接的验证方式:用目标用户登录后执行 SELECT 1;,看到 ERROR 1820 才算策略落地。任何没触发这个错误的配置,都等于没生效。










