mysql 8.0+原生支持密码过期策略,通过default_password_lifetime、password_history等参数全局配置,或用alter user password expire对用户单独设置;5.7需依赖事件调度器或应用层轮转实现。

MySQL 8.0+ 如何启用密码过期策略
MySQL 8.0 开始原生支持密码生命周期管理,核心是 password_history、password_reuse_interval 和用户级 password_expired 属性。不是靠定时脚本或外部工具“模拟”,而是数据库自身强制校验。
实操建议:
- 全局开启需在
my.cnf中配置(重启生效):[mysqld]<br>default_password_lifetime = 90<br>password_history = 5<br>password_reuse_interval = 365
- 对已有用户单独设置更灵活:
ALTER USER 'alice'@'%' PASSWORD EXPIRE INTERVAL 60 DAY; - 若只想让密码到期但不强制改(比如先通知),用:
ALTER USER 'bob'@'localhost' PASSWORD EXPIRE NOW;—— 下次登录就提示ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
MySQL 5.7 怎么绕过缺失的原生策略
5.7 没有 default_password_lifetime 等系统变量,也不能直接设 PASSWORD EXPIRE。所谓“定期修改”只能靠外部机制兜底,否则纯属形同虚设。
常见错误现象:
网上很多教程让你改 validate_password_policy 或调 validate_password_length,但这只管强度,跟“到期必须换”完全无关。
可行路径只有两条:
- 用
mysql_event+ 存储过程定期检查mysql.user表的password_last_changed字段,再发通知或锁账号(但无法拦截登录); - 在应用层或运维平台做密码轮转调度,例如用
mysql -e "ALTER USER ... IDENTIFIED BY ..."定期执行,并记录日志 —— 这才是 5.7 下真正落地的方式。
ALTER USER PASSWORD EXPIRE 的实际触发时机
很多人以为设了 PASSWORD EXPIRE 就立刻失效,其实不然:它只在用户下次**成功认证后首次执行语句时**才拦截,且仅拦截非 SET 类命令。
使用场景与陷阱:
- 用户用客户端连上后,执行
SELECT 1;会直接报错,但SET NAMES utf8mb4;仍可成功; - 如果用户连接后一直没发任何 SQL(比如长连接空闲),过期状态不会主动踢出;
-
PASSWORD EXPIRE DEFAULT表示遵循全局default_password_lifetime,不是“不过期”; - MySQL 8.0.19+ 支持
PASSWORD EXPIRE NEVER,但注意:它不等于禁用策略,只是对该用户豁免 —— 其他用户仍受全局约束。
密码历史限制(password_history)为什么常被忽略
设了 password_history = 5 后,用户不能重复使用最近 5 次用过的密码。但这个“历史”只记录在 mysql.user 表的 password_reuse_history 字段里,且只在每次 ALTER USER ... IDENTIFIED BY 时由服务端自动追加 —— 不是靠哈希比对,也不读取旧 binlog 或审计日志。
关键细节:
- 该功能依赖
validate_password插件已加载(MySQL 8.0 默认启用,5.7 需手动安装); - 如果用
SET PASSWORD FOR 'u'@'h' = 'xxx'(老语法),不会写入历史记录,导致策略失效; - 备份恢复后,
password_reuse_history字段内容可能丢失,相当于清空历史 —— 别指望 mysqldump 能完整保留这个元数据。
真正难搞的不是设参数,而是让所有改密操作都走 ALTER USER、确保插件开着、并且接受它只对新密码生效 —— 旧密码哪怕刚用过,只要没进历史列表,就拦不住。











