最稳妥方式是用 show grants for 逐个导出并过滤敏感账号;不可直接 mysqldump mysql.user,因其会同步 account_locked、password_expired 等状态字段及插件信息,导致测试账号锁死、强制改密或静默失败,且缺少 create user 和 flush privileges 易引发权限失效。

直接用 SHOW GRANTS FOR 逐个导出,再人工或脚本过滤掉生产敏感账号,是最稳妥、最兼容的方式。别碰 mysqldump mysql.user,5.7 里字段和插件虽稳定,但 account_locked、password_expired 等字段一旦被同步到测试库,会导致测试账号莫名锁死或强制改密。
为什么不能直接 mysqldump mysql.user 导出权限
看似省事,实则埋雷:mysqldump mysql.user 会原样导出所有字段,包括 account_locked=1、password_expired=1、password_last_changed 这类状态字段。测试环境不需要这些约束——你刚建好测试账号,结果一导入就被锁,或者连上就提示“Your password has expired”,根本没法验证业务逻辑。
更隐蔽的问题是:5.7 中部分账号可能启用了 mysql_native_password 以外的插件(比如手动改过 plugin 字段),而 dump 出来的 INSERT 语句在目标库执行时,若插件未启用或路径不对,会静默失败或报错 ERROR 1064,但你可能只看到 “Query OK”,误以为成功。
列表中的关键风险点:
-
account_locked字段值为Y会被直接写入,测试账号无法登录 -
password_expired为Y会导致首次连接强制改密,CI/CD 脚本直接中断 -
plugin字段若指向不存在的认证插件(如自定义 UDF),导入失败且无明确提示 - 没有自动补全
CREATE USER,仅靠INSERT INTO mysql.user不生效,必须配FLUSH PRIVILEGES
SHOW GRANTS FOR 'user'@'host' 必须显式指定 host 才可靠
MySQL 权限匹配是按 User+Host 组合精确匹配的。'app'@'10.20.%' 和 'app'@'localhost' 是两个完全独立的账号,权限、密码、插件都可能不同。漏掉任意一个,测试环境就少一组权限,问题复现不出来。
常见错误现象:
- 只执行
SHOW GRANTS FOR 'app'(没带@'host'),MySQL 默认匹配'app'@'%',结果导出的是空权限或错误账号 - 脚本中用
SELECT user, host FROM mysql.user生成命令,但没过滤mysql.session、mysql.sys等系统账号,执行时报错中断 - 导出后没检查输出是否含
WITH GRANT OPTION,导致测试用户无法授权给其他测试账号
正确做法:先查出所有需迁移的账号组合,再拼接完整语句。例如:
mysql -Nse "SELECT CONCAT('SHOW GRANTS FOR ''', user, '''@''', host, ''';') FROM mysql.user WHERE user NOT IN ('root', 'mysql.session', 'mysql.sys') AND host NOT IN ('::1', '127.0.0.1')" | mysql -N | sed 's/$/;/g' > grants_test.sql
注意:-N 关闭列名,-s 去除多余空格,sed 补分号——缺一不可,否则后续执行会语法错误。
导出后必须人工检查三类内容
自动生成的脚本不是终点,而是起点。不检查就导入,等于把生产隐患批量复制到测试环境。
重点核对:
-
CREATE USER是否缺失?SHOW GRANTS只输出GRANT,不输出CREATE USER。如果目标测试库没有该用户,执行GRANT会报错ERROR 1133 (42000): Can't find any matching row in the user table。得手动加一行,例如:CREATE USER IF NOT EXISTS 'app'@'10.20.%' IDENTIFIED BY 'test123'; - 是否有
GRANT ... ON *.*或GRANT PROXY?这类高危权限不该出现在测试环境,必须删掉或降级 - 是否混入了生产专用账号?比如
'backup'@'192.168.100.%'、'monitor'@'zabbix-server',它们在测试网段根本不存在,留着只会干扰权限审计
一个实用技巧:用 grep -E "(GRANT.*\*\.|PROXY|backup|monitor)" grants_test.sql 快速定位可疑行。
导入时别忘了 FLUSH PRIVILEGES
这是最容易被跳过的一步。即使你把所有 CREATE USER 和 GRANT 都执行成功了,MySQL 服务端内存里的权限缓存不会自动更新。测试人员连上去发现“明明授了 SELECT,却报 Access denied”,大概率就是这一步漏了。
正确顺序:
- 先执行
CREATE USER语句(可从grants_test.sql中提取) - 再执行所有
GRANT语句 - 最后单独执行
FLUSH PRIVILEGES;—— 注意,不要把它写在 SQL 文件末尾跟在GRANT后面,因为某些GRANT可能失败,而FLUSH仍会执行,造成“部分生效、部分失效”的混乱状态
复杂点在于:5.7 的 authentication_string 字段虽然存在,但实际密码校验仍走 password 字段(向后兼容)。如果你在导出前改过用户密码(比如用 ALTER USER),要确认 SHOW GRANTS 输出里没混进 8.0 风格的 IDENTIFIED WITH caching_sha2_password——这种语句在 5.7 上会直接报错,必须手动替换为 IDENTIFIED BY 格式。











