ALTER USER PASSWORD EXPIRE INTERVAL 是唯一生效方式;MySQL 8.0.12+ 中让已有用户90天后必须改密,只能显式执行ALTER USER命令设置,default_password_lifetime对老用户完全无效,且策略依赖准确的password_last_changed时间戳。

ALTER USER PASSWORD EXPIRE INTERVAL 是唯一生效方式
MySQL 8.0.12+ 中,让已有用户「90 天后必须改密」,只能靠 ALTER USER 显式设置,没有后台定时任务、也不靠配置文件自动轮转。全局变量 default_password_lifetime 对已有用户完全无效,只影响后续 CREATE USER 的默认值。
常见错误是执行完 SET GLOBAL default_password_lifetime = 90 就以为万事大吉——其实老用户依然永不过期。
-
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;:从password_last_changed时间起算,90 天后登录即拦截 -
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE;:立即过期,下次登录就卡在ERROR 1820 (HY000) -
ALTER USER 'app_user'@'localhost' PASSWORD EXPIRE NEVER;:取消过期(注意不是DEFAULT)
password_last_changed 不更新会导致策略“失效”
MySQL 判断是否过期,只看 mysql.user 表里的 password_last_changed 字段。如果用户密码是用 SET PASSWORD 或旧版 mysqladmin 改的,这个时间戳不会变——结果就是:密码明明刚改,却因倒计时没重置而第二天就报错。
正确做法是改密时强制刷新时间戳:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 用
ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'newpass';替代SET PASSWORD - 如果已用
SET PASSWORD改过密,补一句ALTER USER 'app_user'@'localhost' IDENTIFIED BY 'samepass';(密码不变也行),触发时间戳更新 - 查询验证:
SELECT user, host, password_last_changed, password_lifetime FROM mysql.user WHERE user = 'app_user';
应用连接池和登录拦截时机容易误判
密码过期检查发生在「认证成功后、执行第一条非 SET 语句前」,不是连接建立时,也不是 SQL 执行中。这意味着:
- HikariCP、Druid 等连接池复用旧连接时,不会触发检查;真正拦截发生在连接重建(比如空闲超时后新建连接)那一刻
- 登录后执行
SET NAMES utf8mb4或SET autocommit = 1仍能通过,容易让人误以为“策略没生效” - 客户端报错是
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement,不是认证失败 - 拥有
ALTER USER权限的用户可自行绕过,执行ALTER USER ... PASSWORD EXPIRE NEVER即可禁用
批量设置和 root 账户要特别小心
不能用脚本遍历 mysql.user 表然后拼 SET PASSWORD——那只是改密,不控制过期。真要批量操作,得动态生成 ALTER USER 语句:
- 查所有未设过期策略的普通用户:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' PASSWORD EXPIRE INTERVAL 90 DAY;') FROM mysql.user WHERE password_lifetime IS NULL AND user NOT IN ('root', 'mysql.infoschema'); -
root账户务必显式设为PASSWORD EXPIRE NEVER,否则高可用切换、监控脚本可能因密码过期中断 -
default_password_lifetime = 0表示全局禁用过期,此时即使对用户执行了PASSWORD EXPIRE也会失效;查当前值:SELECT @@default_password_lifetime;
最常被忽略的一点:策略生效依赖 password_last_changed 时间戳准确,而这个字段只在 ALTER USER IDENTIFIED BY 或创建用户时更新,SET PASSWORD 完全不碰它——很多人卡在这一步,反复执行 PASSWORD EXPIRE 却始终不拦截。










