password_expired字段仅标识手动设为过期的用户,无法反映自动过期状态;判断是否过期需结合password_last_changed与default_password_lifetime计算。

查 password_expired 字段只能看到“被手动设为过期”的用户
执行 SELECT user, host, password_expired FROM mysql.user WHERE password_expired = 'Y'; 只能抓出那些被显式执行过 ALTER USER ... PASSWORD EXPIRE 的账号。这类用户一登录就会立刻报 ERROR 1820,必须重置密码才能继续操作。
但这个字段对“自动过期”完全不敏感:password_expired = 'N' 不代表安全,它只是说明没被人工标记过期——而真正决定是否过期的是 password_last_changed 和全局 default_password_lifetime 的组合计算结果。
- MySQL 5.7 及更早版本没有
password_expired字段,查询会直接报错 - 该字段是字符串类型(
'Y'/'N'),不是布尔值,不能用= 1或IS TRUE判断 - 即使你刚用
ALTER USER ... PASSWORD EXPIRE INTERVAL 90 DAY设置了自动过期,password_expired仍为'N'
判断“是否已过期或即将过期”得靠 password_last_changed + default_password_lifetime
真正可靠的检查逻辑是:取 password_last_changed,加上 @@default_password_lifetime 天,再和当前时间比大小。MySQL 服务端就是这么判定的。
例如查 7 天内将过期或已过期的用户:
SELECT user, host, password_last_changed,
TIMESTAMPADD(DAY, @@default_password_lifetime, password_last_changed) AS expires_at
FROM mysql.user
WHERE password_last_changed IS NOT NULL
AND @@default_password_lifetime > 0
AND TIMESTAMPADD(DAY, @@default_password_lifetime, password_last_changed)
-
password_last_changed是 UTC 时间,和服务器时区设置无关 - 如果
@@default_password_lifetime返回0,说明全局策略禁用,这条查询不会返回任何结果 - 返回空结果 ≠ 没有过期用户,可能只是
password_last_changed为NULL(比如用户从未用合法方式改过密)
SHOW CREATE USER 不显示过期信息,别指望它
SHOW CREATE USER 'u1'@'localhost'; 输出的是建账语句快照,只含认证插件、SSL、资源限制等静态定义,**完全不包含 password_last_changed、password_lifetime 或任何运行时状态字段**。
运维工具界面里显示的“最后修改时间”,底层仍是查 mysql.user 表——不是靠解析 SHOW CREATE USER 得来的。
- 哪怕你刚执行完
ALTER USER ... PASSWORD EXPIRE INTERVAL 30 DAY,SHOW CREATE USER输出里也绝不会出现PASSWORD EXPIRE关键字 - 该命令无法替代对
mysql.user的直接查询,也不能用于监控或告警脚本
普通用户查不到自己的过期时间,权限模型默认屏蔽
即使你是 u1,想查自己最后一次改密时间,执行 SELECT password_last_changed FROM mysql.user WHERE User='u1'; 也会失败——报 Access denied 或返回空结果。这不是 bug,是 MySQL 8.0+ 的权限设计:只有 root 或具备 SELECT 权限的管理员能读 mysql.user 表。
- 应用层如需感知密码状态,必须由 DBA 提供专用视图或 API,不能让业务账号直查系统表
-
password_last_changed = NULL表示该用户创建后从未通过ALTER USER或SET PASSWORD合法改过密,可能是初始化时绕过机制直接写authentication_string - 想追溯谁、什么时候改的密码?
password_last_changed只存最后一次,历史记录得靠外部审计日志或 binlog 解析
password_expired = 'Y' 做告警,漏掉的自动过期用户远多于捕获的。











