GRANT 语句的基本写法和权限粒度
MySQL 的权限控制是分层的,
GRANT不是“给用户赋一个权限”那么简单,而是必须明确「作用对象」+「权限类型」+「生效范围」。漏掉任何一项都可能授错权,或根本不起作用。
常见错误是只写
GRANT SELECT ON *.*却没指定用户和主机,结果命令执行成功但权限没落到目标账户上。 权限类型要精确:比如
SELECT、
INSERT、
UPDATE是表级;
CREATE、
DROP是数据库级;
RELOAD、
SHUTDOWN是全局管理权限 作用对象格式为
db_name.tbl_name,
*表示所有库,
*.*表示所有库所有表,
`mydb`.*表示 mydb 库下所有表(注意反引号用于转义含特殊字符的库名) 用户标识必须带主机:如
'appuser'@'192.168.1.%'或
'admin'@'localhost',不写主机名默认是
'user'@'%',但新版本 MySQL 8.0+ 默认禁用
%匹配本地连接
MySQL 8.0+ 与旧版本在权限语法上的关键差异
MySQL 8.0 彻底重构了权限系统,最大的变化是把以前隐式包含的权限(如
USAGE)显式化,并废弃了部分旧语法。直接沿用 5.7 的脚本在 8.0 上会报错。
典型报错:
ERROR 1410 (42000): You are not allowed to create a user with GRANT—— 这是因为 8.0 默认关闭了
create_user_priv自动授予,且要求先
CREATE USER再
GRANT。 必须先显式创建用户:
CREATE USER 'dev'@'%' IDENTIFIED BY 'p@ssw0rd';再授权:
GRANT SELECT, INSERT ON `testdb`.* TO 'dev'@'%';刷新权限仍需:
FLUSH PRIVILEGES;,但仅在手动修改
mysql.user表后才真正必要;用
GRANT命令操作后权限立即生效
WITH GRANT OPTION依然可用,但授予该权限的用户不能跨主机代理(例如
'a'@'%' WITH GRANT OPTION无法给
'b'@'10.0.0.5'授权)
常见误授场景:哪些权限看起来安全实则危险
开发常以为只给
SELECT就够安全,但忽略元数据访问和执行上下文风险。有些权限表面普通,组合使用却可导致信息泄露甚至提权。
SHOW DATABASES:默认关闭,但一旦开启,用户能看到所有库名,可能暴露测试/备份库路径
FILE权限:允许读写服务器文件系统(如
SELECT ... INTO OUTFILE或
LOAD DATA INFILE),配合
SELECT可能读取
/etc/passwd或 MySQL 配置文件
PROCESS:能看到其他用户正在执行的 SQL,含敏感条件或临时表名
EXECUTE:如果数据库里有定义好的存储过程,且该过程内含
INSERT/UPDATE,那么仅授
EXECUTE就等价于间接授了写权限
验证权限是否生效的可靠方式
别只信
SHOW GRANTS FOR 'u'@'h';的输出,它只显示「被授予的语句」,不反映最终生效权限(比如被
REVOKE后未刷缓存,或角色权限未激活)。最准的方式是切到该用户连接后实测。 用目标用户登录:
mysql -u dev -p -h db.example.com检查当前有效权限:
SELECT * FROM INFORMATION_SCHEMA.ROLE_TABLE_GRANTS WHERE GRANTEE = "'dev'@'%'" \G(8.0+ 角色场景)或更通用的
SHOW GRANTS;直接尝试敏感操作,例如:
DROP TABLE IF EXISTS test; -- 看是否报 ERROR 1142注意:权限检查是逐层匹配的,MySQL 优先匹配最具体的规则(如
'u'@'192.168.1.100'比
'u'@'%'优先),所以主机名精度比用户名更重要
权限系统里最易被忽略的是「撤销后残留」——
REVOKE不会自动清除用户账号,也不会影响已建立的活跃连接。连接保持期间旧权限仍然有效,直到断开重连。
