最准确的方法是先查mysql.user表的grant_priv字段再用show grants验证,因角色继承、缓存延迟或手动改表可能导致字段与实际行为不一致;grant_priv仅反映全局显式授权,mysql 8.0+需额外查role_edges和激活状态,并通过实操grant语句最终确认权限是否生效。

直接查 mysql.user 表的 Grant_priv 字段最准,但必须配合 SHOW GRANTS 验证是否真能授予权限——因为角色继承、缓存延迟或手动改表都可能导致字段与实际行为不一致。
查 Grant_priv 字段只反映全局显式授权
MySQL 的 GRANT OPTION 权限不走 mysql.db 或 mysql.tables_priv,只存在 mysql.user 表的 Grant_priv 列,值为 'Y' 或 'N'。执行以下语句可快速列出所有可能拥有该权限的账号:
SELECT User, Host FROM mysql.user WHERE Grant_priv = 'Y';
注意点:
-
Grant_priv是大小写敏感字段名,不能写成grant_priv或GRANT_PRIV - 执行者需有
SELECT ON mysql.*权限,普通用户默认没有,得用root或带SYSTEM_USER角色的账号 - 结果中的
Host必须和登录时完全一致('localhost'≠'127.0.0.1') - 该查询只反映“被直接授予”的状态,不体现角色继承路径(MySQL 8.0+)
MySQL 8.0+ 必须补查角色继承链
在 8.0 引入角色后,用户可能没直连 Grant_priv = 'Y',却通过角色间接获得 GRANT OPTION。此时仅查 mysql.user 会漏掉真实可授权账号。
操作步骤:
- 先查谁绑了角色:
SELECT FROM_USER, FROM_HOST, TO_ROLE FROM mysql.role_edges; - 对每个
TO_ROLE,执行SHOW GRANTS FOR 'role_name'@'%';(注意:角色名必须带主机名,否则报错) - 若输出含
GRANT ... WITH GRANT OPTION,说明该角色链提供授予权限;再反查哪些用户被授了这个角色:SELECT User, Host FROM mysql.user u JOIN mysql.role_edges r ON u.User = r.FROM_USER AND u.Host = r.FROM_HOST WHERE r.TO_ROLE = 'xxx'; - 用户必须已执行
SET ROLE 'xxx';才能激活该权限,否则查到也无效
SHOW GRANTS 输出才是当前会话的真实依据
SHOW GRANTS FOR 'u'@'h' 输出的是 MySQL 当前内存中解析出的、可执行的授权语句,比 mysql.user 表快照更贴近实际行为。但它也有局限:
- 不会显示未激活的角色权限(即使角色已绑定,但没
SET ROLE) - 刚执行过
GRANT ... WITH GRANT OPTION但没FLUSH PRIVILEGES,mysql.user表里可能还是'N',而SHOW GRANTS已显示新权限——此时它更准 - 系统账户如
mysql.session、mysql.infoschema虽然Grant_priv = 'Y',但不能登录,也不应计入“可管理用户”范围
验证是否真能用:登录后执行一条 GRANT 就知道
字段和语句都可能是过期快照或配置残留,最可靠的方式是模拟操作:
- 用目标账号登录:
mysql -u target_user -p - 尝试执行一条最小粒度的授权:
GRANT SELECT ON testdb.* TO 'dummy'@'localhost'; - 若成功,说明
GRANT OPTION确实生效;若报错ERROR 1045 (28000): Access denied,则权限未激活或受限于其他策略(如read_only=ON)
回收权限时要注意:删掉一个带 GRANT OPTION 的角色,并不会自动清除已通过该角色获得权限的用户字段,也不会让已激活的会话立即失效。











