MySQL回收权限必须显式指定目标权限与粒度,不可依赖“先全撤再重授”,否则会导致权限空窗、角色权限遗漏、细粒度权限残留及GRANT OPTION未清除等风险;需结合SHOW GRANTS验证并重连生效。

不能靠“先撤 ALL 再重授”来精准回收部分权限——这会引入权限空窗、并发风险和隐式覆盖
REVOKE ALL PRIVILEGES 会清掉所有显式授予的权限,但不保证“干净”
执行 REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'u1'@'%' 后,该用户在 *.* 粒度上的所有权限(如 SELECT、INSERT、PROCESS、RELOAD)都会被移除。但要注意:
- 如果用户还通过角色(
ROLE)继承了权限,这条语句完全不影响角色所带的权限 - 如果用户在更细粒度上还有独立授权(比如
SELECT ON sales.orders),而你只撤了*.*,那这张表的 SELECT 权限依然存在 - 撤销后立即执行
GRANT SELECT ON *.* TO 'u1'@'%',中间存在毫秒级无权限窗口——已有连接可能报错,新连接若在重授前建立,会因无任何权限(连 USAGE 都还在)而只能登录、无法做任何操作
想只收回某几个权限?必须显式写出来,别绕弯
例如:用户当前有 SELECT, INSERT, UPDATE, DELETE, CREATE,你想只收回 CREATE 和 DELETE,正确做法是:
REVOKE CREATE, DELETE ON *.* FROM 'u1'@'%'- 不是先
REVOKE ALL PRIVILEGES ON *.* FROM 'u1'@'%',再GRANT SELECT, INSERT, UPDATE ON *.* TO 'u1'@'%' - 后者多出两步、两次网络往返、一次潜在权限丢失;且若重授语句漏写某个权限(比如少写了
UPDATE),就真丢了 - 尤其在自动化脚本里,“先全撤再重授”极易因变量拼接错误或权限列表未同步导致权限误删
GRANT OPTION 和 ALL PRIVILEGES 是分离的,必须一起处理
ALL PRIVILEGES 明确不包含 GRANT OPTION。如果你只执行:
-
REVOKE ALL PRIVILEGES ON *.* FROM 'u1'@'%'→GRANT OPTION还在 - 该用户仍能对别人执行
GRANT,等于留了个活口 - 真正安全的做法是始终带上
GRANT OPTION:REVOKE ALL PRIVILEGES, GRANT OPTION ON *.* FROM 'u1'@'%' - 如果只想收部分权限但保留转授权能力,也得显式保留:
REVOKE CREATE, DROP ON *.* FROM 'u1'@'%';—— 不动GRANT OPTION即可
验证比执行更重要,SHOW GRANTS 不等于实时生效
执行 REVOKE 后立刻跑 SHOW GRANTS FOR 'u1'@'%',看到输出里没了目标权限,不代表已生效:
- 旧连接仍持有原权限缓存,需断开重连才体现变化
- 若用的是连接池(如 HikariCP、Druid),得触发连接重建或等待连接超时释放
-
SHOW GRANTS查的是权限元数据,不是运行时状态;它告诉你“系统记着你该有什么”,但不反映“当前会话实际能做什么” - 最稳妥验证方式:新开一个客户端,用该用户登录,执行对应操作(如
CREATE TABLE test(a INT))看是否报ERROR 1142 (42000): CREATE command denied
真正麻烦的从来不是语法写错,而是以为撤掉了、其实没生效,或者撤得太狠、忘了角色和细粒度授权的存在。MySQL 的权限模型是叠加的,不是覆盖的——你删掉一层,底下还有好几层可能托着它。


















