必须用alter user显式设置每个用户,set password只更新密码哈希、不修改password_expired等元数据,故完全无效;default_password_lifetime仅影响新用户,对已有用户不起作用。

必须用 ALTER USER 显式设置每个用户,SET PASSWORD 完全无效,全局变量 default_password_lifetime 对已有用户不起作用。
为什么 SET PASSWORD 不能触发密码过期
它只更新密码哈希值,不碰 password_expired、password_last_changed 这些元数据字段。执行后用户仍能登录,过期策略毫无效果,甚至可能让倒计时“卡在旧时间点”,第二天就报 ERROR 1820。
-
SET PASSWORD FOR 'u'@'h' = 'xxx'在 8.0 中已弃用,且PASSWORD()函数彻底移除,写进去会直接报错 - 真正重置密码并保留策略:用
ALTER USER 'u'@'h' IDENTIFIED BY 'xxx' - 重置密码并取消过期:加
PASSWORD EXPIRE NEVER,不是DEFAULT
如何对已有用户设置 90 天后强制改密
default_password_lifetime 只影响新用户,老用户一个都不管——哪怕你刚给 root 改过密码,也得手动覆盖。
- 立即生效:执行
ALTER USER 'alice'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY - 注意 host 必须完全匹配:
'alice'@'localhost'和'alice'@'%'是两个账户 - 批量处理老用户?只能查表生成语句:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' PASSWORD EXPIRE INTERVAL 90 DAY;') FROM mysql.user WHERE password_expired = 'N' AND password_last_changed IS NOT NULL - 别漏掉
password_last_changed IS NOT NULL条件,否则可能误操作空密码或匿名用户
为什么用户登录后没被拦住
看到 password_expired = 'Y' 不代表策略真起效。MySQL 实际拦截有三个硬性前提:
- 该用户没有
ALTER USER权限(有则跳过重置) -
@@default_password_lifetime非零(若为 0,PASSWORD EXPIRE实际不生效) - 连接未带
--init-command类自动执行逻辑(极少见但会绕过) - 拦截发生在认证成功后、首次执行非
SET语句时(比如SELECT 1),SET NAMES仍能通过,容易误判
验证是否真正生效的最简方式
别只查 mysql.user 表,直接用该账号登录后立刻执行一条非 SET 语句:
- 运行
mysql -u alice -p→ 输入密码 → 紧接着敲SELECT 1; - 如果返回
ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.,才说明策略落地了 - 此时唯一合法操作是
ALTER USER USER() IDENTIFIED BY 'newpass',其他任何 DML/DDL 都会被拒
真正麻烦的不是设过期,而是线上服务账号(如应用连接池用的 app_user)一旦过期,连接池不会自动重认证,直接卡死。这类账号建议从一开始就设 PASSWORD EXPIRE NEVER,别混进统一策略里。











