grant必须显式指定库名(如app_production.),不可用.*或省略库名,否则会误授系统库权限;需先revoke全局权限再授予业务库权限,并注意mysql 8.0+需额外控制information_schema访问。

GRANT 必须写明库名,不能用 *.* 或省略
给开发者只开某个 Schema 的权限,最常见错误是写成 GRANT SELECT ON *.* 或漏掉库名直接 GRANT SELECT ON *。这样等于放行全部数据库,包括 mysql、performance_schema 等系统库——哪怕查不到数据,也会暴露结构、用户列表甚至密码哈希(如 mysql.user 表字段未加密时)。
正确做法是显式写出库名和通配符:GRANT SELECT, INSERT, UPDATE ON app_production.* TO 'dev'@'10.0.0.%'。注意三点:
-
app_production不能加单引号,有特殊字符才用反引号包裹,比如`my-app`.* -
'dev'@'10.0.0.%'的 host 必须匹配真实连接来源,%不匹配 IPv6 或带端口的地址 - 如果开发者要用
USE app_production,至少得有一个库级权限(比如SELECT),USAGE单独不够
旧权限不自动覆盖,必须先 REVOKE 再 GRANT
执行新 GRANT 不会删掉旧权限。比如用户之前被授过 GRANT SELECT ON *.*,你现在只给 app_production.*,他依然能扫 INFORMATION_SCHEMA.TABLES 查所有库的表名,甚至跨库 JOIN。
安全闭环操作顺序是:
- 先查当前权限:
SHOW GRANTS FOR 'dev'@'10.0.0.%' - 如果有全局权限,必须显式回收:
REVOKE ALL PRIVILEGES ON *.* FROM 'dev'@'10.0.0.%' - 再授业务库权限:
GRANT SELECT, INSERT ON app_production.* TO 'dev'@'10.0.0.%' - 最后确认:
SELECT * FROM mysql.db WHERE User='dev' AND Db='app_production'\G,看Select_priv是否为Y
MySQL 8.0+ 要显式控制 INFORMATION_SCHEMA 访问
只给 app_production.* 权限后,开发者执行 SHOW CREATE TABLE users 或 ORM 自动查列信息时,可能报错 ERROR 1142 (42000): SELECT command denied。这不是权限没生效,而是 MySQL 8.0+ 默认收紧了对 INFORMATION_SCHEMA 的访问。
如果需要支持元数据查询(比如开发工具连上就能看到表结构),得额外授权:
GRANT SELECT ON INFORMATION_SCHEMA.TABLES TO 'dev'@'10.0.0.%'GRANT SELECT ON INFORMATION_SCHEMA.COLUMNS TO 'dev'@'10.0.0.%'-
GRANT SELECT ON INFORMATION_SCHEMA.STATISTICS TO 'dev'@'10.0.0.%'(如果用到索引信息)
不想暴露元数据?MySQL 8.0.12+ 可以直接回收:REVOKE SELECT ON INFORMATION_SCHEMA.* FROM 'dev'@'10.0.0.%';5.7 及更早版本做不到这点,只能靠限制业务库权限间接压制。
权限生效不需要 FLUSH PRIVILEGES
只要用 GRANT 或 REVOKE 操作,权限立即生效,新连接直接继承。只有当你手动改了 mysql.user 表才需要 FLUSH PRIVILEGES——那是危险操作,官方明确不推荐。
如果发现权限没变,优先检查:
- 用户名和 host 是否完全匹配(
'dev'@'10.0.0.%'和'dev'@'%'是两个账号) - 是否在错误实例上执行(比如连的是从库,但权限只在主库改了)
- 客户端是否缓存了旧连接(重连一次最直接)
真正容易被忽略的点是:权限粒度和版本差异交织在一起。比如在 8.0 上开了 INFORMATION_SCHEMA.COLUMNS,但在 5.7 上这条语句根本不可授权——不是你操作错了,是底层能力不存在。别假设一套 SQL 能跨版本通用。











