MySQL 8.0+用户密码过期后连接被拒绝,因authentication_string过期导致账户锁死;需用ALTER USER ... PASSWORD EXPIRE NEVER禁用策略,或设default_password_lifetime=0持久生效。

MySQL 8.0+ 用户密码过期后,连接会直接被拒绝
MySQL 8.0 引入了 password_expired 状态和 password_lifetime 策略,不是“权限过期”,而是“账户被锁死”——用户仍存在、权限也还在,但只要 authentication_string 过期,mysql 客户端连上去就会报错:ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
常见错误现象:
- 应用突然连不上数据库,日志里反复出现
Access denied for user,但用户名密码没改过 - 用
mysql -u xxx -p登录时输对密码也进不去,提示要重置密码 -
SELECT User, Host, password_last_changed, password_lifetime FROM mysql.user;查出来某用户password_last_changed是很久以前,且password_lifetime不为0或NULL
ALTER USER ... PASSWORD EXPIRE NEVER 关闭自动过期
最常用、最直接的解法:关掉过期机制。注意这不是“延长有效期”,而是彻底禁用该策略。
实操建议:
- 对已有用户执行:
ALTER USER 'app_user'@'%' PASSWORD EXPIRE NEVER; - 新建用户时就禁用:
CREATE USER 'app_user'@'%' IDENTIFIED BY 'xxx' PASSWORD EXPIRE NEVER; - 全局默认关闭(推荐):
SET PERSIST default_password_lifetime = 0;—— 这样后续所有新用户都不再自动启用过期 - 别用
SET GLOBAL default_password_lifetime = 0;,重启后失效;SET PERSIST会写入mysqld-auto.cnf,持久生效
password_lifetime = 0 和 NULL 的区别很关键
MySQL 对这两个值的处理完全不同,容易踩坑:
-
password_lifetime = 0:明确表示“永不过期”,是安全、可预期的行为 -
password_lifetime = NULL:继承全局default_password_lifetime值,如果全局设的是 90,那这个用户还是每 90 天过期 - 查当前策略:
SELECT @@global.default_password_lifetime; - 修改全局策略后,已有用户的
password_lifetime不会自动更新,必须显式ALTER USER才生效
应用连接池不支持密码重置,所以不能依赖“用户自己改”
很多 Java 应用用 HikariCP、Druid,Python 用 SQLAlchemy + pymysql,它们连接时只传用户名密码,不支持在连接过程中响应 MySQL 的“请重置密码”指令。一旦用户过期,连接池初始化失败,整个服务起不来。
这意味着:
- 不能指望运维手动去跑
ALTER USER ... PASSWORD EXPIRE DEFAULT;来“恢复”——应用根本连不进去执行这条语句 - 不要在生产环境对连接池使用的账号启用
PASSWORD EXPIRE INTERVAL 30 DAY这类策略 - 如果真需要定期轮换密码,得靠外部工具(如 HashiCorp Vault)动态下发,并配合应用侧的凭据刷新逻辑,而不是靠 MySQL 自动锁死
真正难处理的不是怎么设过期,而是过期后没人能登录进去修——尤其是 root 账户也被设了 PASSWORD EXPIRE,又没留其他高权限账号时,只能停库加 --skip-grant-tables 启动硬修复。


















