MySQL中“可见”与“可访问”分离:用户有任意库权限时SHOW DATABASES仍列全库名,但USE或SELECT其他库会报Access denied;彻底隐藏需禁用SHOW DATABASES权限并设show_database_privilege=OFF。

直接给答案:只对目标数据库执行 GRANT,不碰 *.*,不授全局权限,再配合 host 限定和 FLUSH PRIVILEGES,就能让用户实际只能操作那个库——但 SHOW DATABASES 仍会列出所有库名,这是正常行为,不是权限失效。
为什么用户还能看到其他数据库名
MySQL 的权限模型里,“可见”和“可访问”是两回事。只要用户有任意数据库的哪怕一个权限(比如 SELECT on app_db.*),SHOW DATABASES 就会返回全部库名(除 information_schema 等系统库可能被隐藏)。这不是漏洞,是设计如此。
真正起作用的是后续操作:
-
USE other_db;→ 报错Access denied for database 'other_db' -
SELECT * FROM other_db.t1;→ 同样报Access denied
想彻底隐藏库名,得用 MySQL 8.0.29+ 的 show_database_privilege 系统变量,并确保用户没被授予 SHOW DATABASES 权限——但这属于进阶控制,多数场景没必要。
GRANT 语句必须写全数据库前缀和对象范围
错误写法常见于复制粘贴或凭记忆敲命令:
-
GRANT SELECT ON app_db TO 'u1'@'localhost';→ 缺.*,语法错误 -
GRANT SELECT ON *.app_table TO 'u1'@'localhost';→ 通配符位置错,不生效 -
GRANT SELECT ON *.* TO 'u1'@'localhost';→ 开放全部库,立刻失效最小权限原则
正确写法只有一种模式:GRANT <priv> ON <db_name>.<object> TO 'user'@'host';
其中 <object> 可以是 *(整个库)、table_name(单表)或 PROCEDURE sp_name(存储过程,见下一条)。
别漏掉 PROCEDURE/FUNCTION 的 EXECUTE 单独授权
如果你的业务用了存储过程或函数,仅授 SELECT on app_db.* 是不够的。调用 CALL app_db.sp_calc() 或 SELECT app_db.f_total(100) 会失败,报错 ERROR 1370 (42000): execute command denied。
必须额外、显式授权:
GRANT EXECUTE ON PROCEDURE app_db.sp_calc TO 'u1'@'localhost';GRANT EXECUTE ON FUNCTION app_db.f_total TO 'u1'@'localhost';
注意两点:
- MySQL 5.7 不支持该语法,必须 8.0.16+;
- 必须先有
USAGE ON app_db.*,否则 EXECUTE 授权静默失败(SHOW GRANTS看得到,但实测调用报错)。
容易被忽略的 host 匹配和插件兼容性
用户连不上,90% 不是权限问题,而是账号没匹配上:
-
'u1'@'localhost'和'u1'@'%'是两个完全不同的账号,不能混用; - 远程连接必须明确指定非
localhost的 host,比如'u1'@'192.168.1.%'或'u1'@'%'; - MySQL 8.0+ 默认用
caching_sha2_password插件,老客户端(如某些 Python 2.7 环境下的MySQLdb)不支持,建号时应加IDENTIFIED WITH mysql_native_password。
验证是否生效,最可靠的方式不是看 SHOW GRANTS,而是用该用户真实连接后执行:USE app_db;、SELECT COUNT(*) FROM app_db.t_user;、CALL app_db.sp_ping(); —— 任一失败,就回头检查对应环节。


















