背景
客户反馈MySQL账号已经设置成密码永不过期了,但是在登录后总是提示报错
ERROR 1862 (HY000): Your password has expired. To log in you must change it using a client that supports expired passwords.
排查方法
首先检查一下MySQL服务器设置的密码过期时间,可以看到默认密码60天就会过期
mysql> show variables like '%lifetime%';
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| default_password_lifetime | 60 |
+---------------------------+-------+
1 row in set (0.01 sec)
然后检查一下mysql.user表,看看用户说提示报错的账号的配置
mysql> select user,host,password_expired,password_last_changed,password_lifetime from mysql.user where user='user';
+------+------+------------------+-----------------------+-------------------+
| user | host | password_expired | password_last_changed | password_lifetime |
+------+------+------------------+-----------------------+-------------------+
| user | % | N | 2023-02-06 15:03:41 | 0 |
+------+------+------------------+-----------------------+-------------------+
1 row in set (0.00 sec)
password_expired
列为N
这个账号没有被手动设置密码过期password_lifetime
列为0
账号正常应该已经设置成密码永不过期了password_last_changed
列为2023-02-06 15:03:41
上次密码修改时间是2023-02-06
疑点
这个问题诡异的地方就是password_lifetime
列为0。
正常情况下,单个账号的策略会覆盖全局策略。也就是说,如果设置过单个账号密码永不过期,就不会再参考default_password_lifetime
参数值计算密码过期时间。
这个账号如果按照上次密码修改时间+默认60天过期计算的话,确认是密码过期该修改密码了。
分析
排查了半天,后来灵机一动,想起来一种情况:
这个账号是客户自己设置成密码永不过期的,正常我们设置密码永不过期都是使用ALTER USER user PASSWORD EXPIRE NEVER
语句,但是客户会不会用了其他方法呢,比如说直接修改mysql.user
表?
我们知道,MySQL会在启动时将权限表读取到内存中。而直接更新mysql.user
表,不会立即生效,需要再执行FLUSH PRIVILEGES
重新加载权限表。
后来追问客户,客户果然是在navicat界面中直接点击mysql.user
表,把password_lifetime
列改为0,就以为设置成功了。
处理方案
执行FLUSH PRIVILEGES
刷新权限后,果然账号登录时不再报错。
原因
设置密码永不过期的方法错误,只更新了mysql.user
表,没有刷新权限
正确方法
ALTER USER user PASSWORD EXPIRE NEVER;
-- 或者
UPDATE mysql.user SET password_lifetime=0 WHERE user='user';
FLUSH PRIVILEGES;
扩展知识
MySQL的权限修改何时生效
MySQL在启动时将权限表加载到内存中,之后使用内存中的权限表进行访问控制
- 使用账号管理语句,如
ALTER USER/GRANT/REVOKE/SET PASSWORD
等,会重新加载权限表,立即生效。 - 使用
INSERT,UPDATE,DELETE
等更改权限表,不会加载到内存中,不会立即生效。要使这些更改生效,可以:- 重新加载权限表
FLUSH PRIVILEGES
- 重启数据库
- 重新加载权限表
设置密码有效期策略
- 全局有效期更改
SET GLOBAL default_password_lifetime = 60
- 单个账号有效期更改
-- 跟随全局策略
ALTER USER user PASSWORD EXPIRE DEFAULT
-- 密码永不过期
ALTER USER user PASSWORD EXPIRE NEVER
-- 密码N天过期
ALTER USER user PASSWORD EXPIRE INTERVAL N DAY
- 直接设置密码过期
ALTER USER user PASSWORD EXPIRE
密码立刻过期,在账号登录后,必须修改密码才能进行其他操作
使用这种方式设置密码过期后,没有SQL语句直接设置密码不过期,但是可以通过更新mysql.user
的password_expired
列,然后FLUSH PRIVILEGES
刷新权限,变相允许用户继续使用之前的密码