必须使用GRANT SELECT,INSERT ON myapp. TO 'u1'@'192.168.1.%';格式,严格匹配库名加.、精确主机段,并执行FLUSH PRIVILEGES生效,否则权限无效或留后门。

直接给某个用户分配「只操作某一个库」的权限,GRANT 语句本身不难,但错一个字符、漏一个点、少一次刷新,权限就不起作用,甚至开个后门。
ON mydb.* 不能写成 ON mydb 或 ON mydb.**
MySQL 权限系统严格区分「数据库级」和「全局级」范围,ON 后面必须是 database_name.* 形式,* 表示该库下所有表。常见错误包括:
-
GRANT SELECT ON myapp TO 'u1'@'%';→ 报错ERROR 1064 (42000),缺少.* -
GRANT SELECT ON myapp.*.* TO 'u1'@'%';→ 语法错误,最多两个层级(库.表) -
GRANT SELECT ON *.* TO 'u1'@'%';→ 危险!这是跨库只读,可能暴露其他业务库
正确写法只有一种模式:GRANT SELECT, INSERT ON myapp.* TO 'u1'@'192.168.1.%';
‘user’@‘host’ 必须完整匹配连接来源
MySQL 认证时,会把登录用户的 user 和客户端 IP(或主机名)拼成 'user'@'host' 去查 mysql.db 表。如果实际连接用的是 mysql -u u1 -h 10.0.5.20,但授权写的是 'u1'@'localhost',那永远连不上。
-
'u1'@'localhost':仅接受 Unix socket 或127.0.0.1的连接,::1不算在内(IPv6) -
'u1'@'10.0.5.%':匹配10.0.5.1~10.0.5.255,但不匹配10.0.50.1 -
'u1'@'%':允许任意 IP,但禁止通过localhost连接(因为localhost优先走 socket,不走 TCP)
调试时先查 SELECT user, host FROM mysql.user WHERE user = 'u1';,确认记录存在且 host 值与实际连接方式一致。
GRANT 后必须执行 FLUSH PRIVILEGES
哪怕 GRANT 执行返回 Query OK,新权限也不会立即生效——它只是写进了 mysql.db 表,而 mysqld 进程仍从内存缓存读权限。不刷,新连接可能拿到旧权限,已连上的会话完全不受影响。
- 必须显式执行:
FLUSH PRIVILEGES; - MySQL 8.0+ 在部分场景下自动刷新(如改
mysql.user),但改mysql.db仍需手动刷 - 不需要重启 MySQL,也不影响正在运行的查询
- 如果用非 root 用户执行
GRANT,且没开skip-grant-tables,则FLUSH PRIVILEGES也必须由有RELOAD权限的用户执行
验证权限是否真的生效
别只信 SHOW GRANTS 输出,要真连进去试操作。最简验证路径:
- 新开终端,用授权账号连接:
mysql -u u1 -p -h 10.0.5.20 - 执行
USE myapp;→ 应成功 - 执行
SHOW TABLES;→ 应列出表(说明SELECT+SHOW VIEW权限到位) - 执行
INSERT INTO t1 VALUES (1);→ 若报ERROR 1142 (42000): INSERT command denied,说明权限未覆盖到表,或FLUSH没做 - 再连一次
mysql -u u1 -p -h 10.0.5.20 -D otherdb→ 应拒绝访问(证明没越权)
最容易被忽略的是:授权后用户没重连,或者误以为 localhost 能匹配远程 IP —— 这两个点卡住的人最多。











