mysql不支持账号过期后自动禁止连接,密码过期仅在首次执行sql时抛出error 1820,连接仍成功;真禁连需用account lock或max_connect_errors,或8.0.19+的failed_login_attempts+password_lock_time组合。

MySQL 本身不支持“账号过期后自动禁止所有连接请求”这种行为。 密码过期(PASSWORD EXPIRE)只会触发 ERROR 1820,要求用户重置密码,但连接本身是成功的——只要认证通过(比如用旧密码连上),就能执行 ALTER USER;它不会拒绝连接,更不会拦截后续任何 TCP 握手或认证流程。
为什么 ALTER USER PASSWORD EXPIRE 后还能连上?
这是 MySQL 的设计逻辑:过期 ≠ 锁定。只要 authentication_string 还有效、用户未被 ACCOUNT LOCK、且密码未被强制重置,客户端就能完成认证并建立连接。区别只在连接建立后的**第一条语句执行时**抛出 ERROR 1820 (HY000),阻止其他操作。
- 典型现象:
mysql -u app_user -p输对密码能进命令行,但一敲SELECT 1;就报错 - 应用层看到的是“登录成功但无法执行 SQL”,不是“连接被拒”
- 连接池(如 HikariCP)可能反复重试,导致日志刷屏
ERROR 1820
真要禁止连接,必须用 ACCOUNT LOCK 或 max_connect_errors
想让账号彻底无法建立新连接,只有两条路可走,且都和密码过期无关:
-
ALTER USER 'app_user'@'%' ACCOUNT LOCK;—— 立即生效,后续所有认证请求直接返回ERROR 3118 (HY000): Access denied for user 'app_user'@'xxx': account is locked - 配合
max_connect_errors:设为较低值(如SET GLOBAL max_connect_errors = 3;),同一 IP 连续输错 3 次密码后,该 IP 所有连接(包括正确密码)都会被拒绝,错误仍是ERROR 1045 (28000),但本质是服务端丢包,不是认证失败 - 注意:
ACCOUNT LOCK和PASSWORD EXPIRE可同时启用,互不影响,但不能靠后者触发前者
FAILED_LOGIN_ATTEMPTS + PASSWORD_LOCK_TIME 是唯一接近“自动锁”的原生方案
MySQL 8.0.19+ 支持基于失败登录次数的锁定,但它需要两个条件同时满足,缺一不可:
- 用户必须使用
caching_sha2_password或mysql_native_password认证插件(查SELECT plugin FROM mysql.user WHERE user = 'xxx';) - 必须显式设置:
ALTER USER 'app_user'@'%' FAILED_LOGIN_ATTEMPTS 5 PASSWORD_LOCK_TIME 1440;(5 次失败后锁 1440 分钟) - 这个锁定是“真锁”:后续连接直接返回
ERROR 3118,不进认证环节 - 但它是按失败次数触发,不是按“过期时间”触发——没有“90 天没改密就自动锁”这种机制
真正要实现“账号过期即禁连”,得靠外部调度:写个 cron 脚本每天跑 SELECT user, host, password_last_changed, password_lifetime FROM mysql.user,算出已过期用户,再对它们执行 ALTER USER ... ACCOUNT LOCK。别指望 MySQL 自己定时扫表做这事。











