直接revoke update mysql.user无效,因mysql 5.7+硬编码拦截系统表写入;真正需回收的是alter user、create user和grant option权限,并禁用mysql.*的insert/update/delete/drop。

为什么直接 revoke UPDATE mysql.user 没用
很多人试过给普通管理员账号撤掉 UPDATE 权限,以为就能防住改他人密码,结果发现 ALTER USER 'other'@'host' IDENTIFIED BY 'xxx' 还是能执行成功。这不是权限没生效,而是 MySQL 5.7+ 根本不走 mysql.user 表写入路径——它硬编码拦截所有直改系统表的操作,哪怕你显式给了 UPDATE ON mysql.*,执行时也会报 ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation 或类似错误。真正起效的控制点只有两个:是否拥有 ALTER USER 和 CREATE USER 权限。
必须同时回收 ALTER USER 和 CREATE USER
CREATE USER 权限在 MySQL 官方文档里明确说明“隐含 ALTER USER 能力”,也就是说,哪怕你只给了 CREATE USER,用户也能执行 ALTER USER 修改任意账号密码。所以单撤 ALTER USER 不够,必须一起 revoke:
REVOKE ALTER USER, CREATE USER ON *.* FROM 'admin_user'@'%';- 如果该用户还被授予过
GRANT OPTION,也得一并回收:REVOKE GRANT OPTION ON *.* FROM 'admin_user'@'%'; - 确认无残留:
SHOW GRANTS FOR 'admin_user'@'%';输出里不应出现ALTER USER、CREATE USER或GRANT OPTION
别忘了锁死 mysql 系统库的写权限
虽然直改 mysql.user 被服务层拦截,但某些旧脚本或误操作仍可能尝试 INSERT/UPDATE/DELETE 系统库表。为防绕过,显式收回这些权限更稳妥:
REVOKE INSERT, UPDATE, DELETE, DROP ON mysql.* FROM 'admin_user'@'%';- 注意:不能 revoke
USAGE,它是默认连接权限,且无法被撤销;也不建议授SELECT给mysql库,除非真有审计需求 - MySQL 8.0+ 中
mysql.user的Password字段已移除,密码全存在authentication_string,但字段级权限控制无效,所以重点仍在语句级权限(ALTER USER)
真正的最小权限组合是什么
一个能管业务库但碰不了用户系统的“半管理员”,应该只保留这些:
- 对业务库的必要操作权,例如:
GRANT SELECT, INSERT, UPDATE, DELETE ON app_db.* TO 'admin_user'@'%'; - 显式授予
USAGE(虽默认存在,但写明更清晰):GRANT USAGE ON *.* TO 'admin_user'@'%'; - 绝不授
ON *.*,绝不授ALL PRIVILEGES,绝不授任何涉及mysql、information_schema、performance_schema的权限 - 如果要用角色简化管理,创建专用角色:
CREATE ROLE 'app_admin'; GRANT SELECT, INSERT ON app_db.* TO 'app_admin'; GRANT 'app_admin' TO 'admin_user'@'%';
最易被忽略的是 CREATE USER 的隐含能力——它不像看起来只是“建新账号”,而是等价于拿到用户管理总开关。生产环境里,哪怕只漏掉这一项,就等于把密码修改权白送给对方。











