mysql.user表的数据准确性问题

mysql.user这个系统表中有些字段的数据是不准确的(或者说是不一定准确,这样表达更严谨一点)。这是一个让人头疼的问题,下面简单述说一下问题,主要是mysql.user表中的password_lifetime,password_reuse_history,password_reuse_time这几个字段的数据都不一定准确。

下面简单演示一下,当前测试环境为MySQL 8.0.35。

mysql> show global variables like 'default_password_lifetime';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| default_password_lifetime | 180   |
+---------------------------+-------+
1 row in set (0.00 sec)

mysql> show  global variables like 'password_history';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| password_history | 6     |
+------------------+-------+
1 row in set (0.01 sec)

mysql> show global variables like 'password_reuse_interval';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| password_reuse_interval | 365   |
+-------------------------+-------+
1 row in set (0.00 sec)

mysql>

此时,如果我创建一个用户test,如下所示,你会看到password_lifetime值为null,password_reuse_history的值为null,这个值明显不正确,是不是Bug呢?

mysql> CREATE USER `test`@`192.168.7.10%` IDENTIFIED BY  'Kjdh#234289dj';
Query OK, 0 rows affected (0.03 sec)

mysql> select user
    ->       ,host
    ->       ,account_locked 
    ->       ,password_last_changed
    ->       ,password_expired
    ->       ,password_lifetime
    ->       ,plugin 
    ->       ,password_reuse_history
    ->       ,Password_reuse_time
    -> from mysql.user
    -> where user='test'\G
*************************** 1. row ***************************
                  user: test
                  host: 192.168.7.10%
        account_locked: N
 password_last_changed: 2024-08-20 11:05:39
      password_expired: N
     password_lifetime: NULL
                plugin: caching_sha2_password
password_reuse_history: NULL
   Password_reuse_time: NULL
1 row in set (0.01 sec)

mysql> 

搜索相关资料时,发现已经有人反馈这个问题了,Bug #112128 The parameters such as MySQL password_history does not take effect[1] 但是官方的解释是这是一个"正常现象",它不是一个Bug,具体解释如下所示:

The way it works is as follows:
1. There is the global system variable default_password_lifetime that specifies the policy for all accounts that are set to use the default password lifetime. This is done by ALTER USER PASSWORD EXPIRE DEFAULT. In the system table (not that it matters, but still) this stores a NULL. The NULL value is used as a flag that the account in question does not have a special per user password lifetime. The special per-user password lifetime is set via ALTER USER PASSWORD EXPIRE NEVER (which sets the column to 0) or ALTER USER PASSWORD EXPIRE INTERVAL N DAY (which sets the column to N). 
As a consequence all password lifetimes for all users that do not have a specific password lifetime set will follow the value of the global variable.
And if you store a specific password lifefile for a user account it will "detach" itself from the global variable's value and will be pegged to whatever you've set for it. Until you reset it back to the default of course.

简单翻译如下:

它的工作原理如下:

有一个全局系统变量 default_password_lifetime,它指定了所有使用默认密码生命周期的帐户的策略。这是通过 ALTER USER PASSWORD EXPIRE DEFAULT命令完成的。在系统表中(这并不重要,但还是说一下),它存储了一个 NULL 值。NULL 值被用作一个标志,表示相关的帐户没有特别的用户密码生命周期。 每个用户的特殊的密码生命周期是通过 ALTER USER PASSWORD EXPIRE NEVER(这将列设置为 0)或 ALTER USER PASSWORD EXPIRE INTERVAL N DAY(这将列设置为 N)来设置的。 因此,所有没有设置特定/特殊密码生命周期的用户的所有密码生命周期都将遵循全局变量的值。 如果你为一个用户帐户存储了一个特定的密码生命周期,它将“脱离”全局变量的值,并被固定为你为它设置的任何值。当然,除非你将其重置回默认值。

搜索资料的过程,发现反馈这种现象("bug")的还不止一个,另外一个链接也是反馈了同样的问题,Bug #89349 password_lifetime set to Null for user after changing default_password_lifetime[2]

这里简单说明一下,就是mysql.user中,password_lifetime值为null,表示它使用全局系统变量的值,如果你为某个用户指定了 特殊的密码过期时间,那么mysql.user的password_lifetime字段才会存储特定的密码过期时间。也就是说如果我们创建用户的时候指定密码过期时间,或者使用SQL语句指定用户的密码过期时间,此时mysql.user中的password_lifetime等字段值就是正确的。如下所示

mysql> ALTER USER 'test'@'192.168.7.10%' PASSWORD EXPIRE INTERVAL 60 DAY;
Query OK, 0 rows affected (0.01 sec)

mysql> select user
    ->       ,host
    ->       ,account_locked 
    ->       ,password_last_changed
    ->       ,password_expired
    ->       ,password_lifetime
    ->       ,plugin 
    ->       ,password_reuse_history
    ->       ,Password_reuse_time
    -> from mysql.user
    -> where user='test'\G
*************************** 1. row ***************************
                  user: test
                  host: 192.168.7.10%
        account_locked: N
 password_last_changed: 2024-08-20 11:05:39
      password_expired: N
     password_lifetime: 60
                plugin: caching_sha2_password
password_reuse_history: NULL
   Password_reuse_time: NULL
1 row in set (0.00 sec)

mysql> CREATE USER test1@'%' IDENTIFIED BY 'Kjdh#234289dj' 
    -> PASSWORD EXPIRE INTERVAL 20 DAY 
    -> PASSWORD HISTORY 30  
    -> PASSWORD REUSE INTERVAL 60 DAY;
Query OK, 0 rows affected (0.03 sec)

mysql> select user
    ->       ,host
    ->       ,account_locked 
    ->       ,password_last_changed
    ->       ,password_expired
    ->       ,password_lifetime
    ->       ,plugin 
    ->       ,password_reuse_history
    ->       ,Password_reuse_time
    -> from mysql.user
    -> where user='test1'\G
*************************** 1. row ***************************
                  user: test1
                  host: %
        account_locked: N
 password_last_changed: 2024-08-20 11:57:18
      password_expired: N
     password_lifetime: 20
                plugin: caching_sha2_password
password_reuse_history: 30
   Password_reuse_time: 60
1 row in set (0.00 sec)

mysql>

总结

虽然官方的这种逻辑处理也没有什么大问题,但是不能准确的反映用户密码过期时间,让普通用户也挺困惑的。为什么不能统一致呢?

参考资料
[1]

1: https://bugs.mysql.com/bug.php?id=112128

[2]

2: https://bugs.mysql.com/bug.php?id=89349

扫描上面二维码关注我
如果你真心觉得文章写得不错,而且对你有所帮助,那就不妨帮忙“推荐"一下,您的“推荐”和”打赏“将是我最大的写作动力!
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接.