revoke 不等于权限立刻失效,尤其在长连接、连接池、角色继承场景下;必须严格匹配 grant 粒度与用户标识,显式撤销 grant option,优先通过角色回收权限,并验证 show grants 而非仅信 query ok。

直接执行 REVOKE 不等于权限立刻失效,尤其在长连接、连接池、角色继承场景下,用户可能继续执行被撤销的操作——真正风险不在语句写错,而在“以为撤了,其实还在用”。
REVOKE 必须严格匹配 GRANT 的粒度和用户标识
MySQL 不会自动推断你原本想撤哪一层权限。当初怎么授的,现在就得怎么撤。
- 如果用
GRANT SELECT ON sales.orders TO 'report'@'192.168.1.%'授了单表查权,那就必须写REVOKE SELECT ON sales.orders FROM 'report'@'192.168.1.%';写成REVOKE SELECT ON sales.*就多撤了其他表,写成REVOKE SELECT ON *.*更会波及整个实例 -
'report'@'192.168.1.%'和'report'@'localhost'是两个完全独立的用户,主机名不一致会导致Unknown user错误,或静默失败(因目标用户根本不存在) - 全局权限(如
SUPER、GRANT OPTION)只能在*.*上撤销,写成db.*会直接报语法错误
GRANT OPTION 必须显式单独撤销
ALL PRIVILEGES 不含 GRANT OPTION,这是生产环境最常被忽略的后门漏洞。
- 执行
REVOKE ALL PRIVILEGES ON *.* FROM 'admin'@'%'后,该用户仍能给其他人授权——因为GRANT OPTION还在 - 正确做法是补上:
REVOKE GRANT OPTION ON *.* FROM 'admin'@'%' - 想一步清空?写成:
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'admin'@'%' - 如果用户通过角色获得权限,优先用
REVOKE role_name FROM 'user'@'host',而非逐条撤底层权限
撤销后权限不立即对已有连接生效
已建立的客户端连接(包括应用连接池里的长连接)不会自动丢弃旧权限,它们仍能继续执行被撤销的操作,直到重连。
-
FLUSH PRIVILEGES在 MySQL 8.0+ 中非必需,但验证比刷新更重要——它不是“让撤销生效”的开关,而是强制重载整张权限表 - 务必立刻执行
SHOW GRANTS FOR 'user'@'host',确认输出里已剔除目标权限项;别只信Query OK - 若用户有多个 host 记录(如
'u'@'localhost'和'u'@'127.0.0.1'),需分别验证每条 - 高并发实例中,
FLUSH PRIVILEGES可能引发短暂锁表,应避开业务高峰
仅靠 REVOKE DROP 并不能防住误删表
误删不止靠 DROP TABLE。TRUNCATE、ALTER、CREATE 等操作同样危险,且不受 DROP 权限控制。
-
TRUNCATE TABLE在 MySQL 8.0+ 需单独TRUNCATE权限,旧版则依赖DROP;需额外检查并回收 -
ALTER TABLE ... DROP COLUMN属于ALTER权限范畴,和DROP无关,但也能结构性破坏表 - 有
CREATE权限的用户可新建同名表覆盖原表(若sql_mode允许),效果等同误删 - 更有效的防护是组合策略:关键库设为只读(
SET GLOBAL read_only = ON)、中间件拦截DROP/TRUNCATE、定期导出SHOW CREATE TABLE存 Git 做结构 diff
最易被忽略的点是权限缓存与连接复用之间的隐性窗口——哪怕语句写对、刷新执行、验证通过,只要应用没重连,旧权限就还在生效。这不是 MySQL 的 bug,而是设计使然。











