REVOKE只能撤销显式授予的权限,无法拆解宽权限;若用户有SELECT ON mydb.*,则REVOKE SELECT ON mydb.t1无效,必须先撤宽权限再重授细粒度权限,并重连验证。

REVOKE 只撤销显式授予的权限,不拆解宽权限
MySQL 的权限检查是“叠加生效”,不是“精细切片”。比如用户有 GRANT SELECT ON mydb.* TO 'u'@'%',你执行 REVOKE SELECT ON mydb.t1 FROM 'u'@'%' 语法合法,但实际无效——mydb.* 这个更宽的授权仍然存在,它自动覆盖 t1,MySQL 不会从中“剔除”一张表。
真正能收回单表权限的前提只有一个:该表权限当初是独立用 GRANT SELECT ON mydb.t1 授予的,且没有 mydb.* 或 *.* 等更高粒度权限同时存在。
- 用
SHOW GRANTS FOR 'u'@'%'查当前所有显式授权,确认是否存在更宽范围语句 - 若存在
ON mydb.*,必须先REVOKE SELECT ON mydb.* FROM 'u'@'%',再重新GRANT剩余需要的表 - 别指望 MySQL 自动推导“除了 t1 以外的所有表”——它只认你写过的那条
GRANT
当前连接缓存旧权限,导致“撤了还有效”
执行 REVOKE 后,新连接立即生效,但已存在的客户端连接仍持有旧权限快照。这是最常被误判为“REVOKE 失效”的原因。
- 交互式测试时,必须退出当前
mysql客户端,重新mysql -u u -p登录验证 - 应用侧使用连接池(如 HikariCP、SQLAlchemy)时,需重启服务或清空连接池,否则旧连接持续可用
-
KILL CONNECTION [id]可强制终止活跃会话,但不能替代权限重连逻辑
USAGE 权限无法被撤销,用户仍可登录
USAGE 是 MySQL 用户的“存在标识”,不是操作权限。创建用户时自动赋予,且不能被 REVOKE 显式移除。
执行 REVOKE ALL PRIVILEGES ON *.* FROM 'u'@'%' 后,用户依然能成功连接,只是执行任何 DML/DQL 都会报错:ERROR 1142 (42000): SELECT command denied。
- 若要彻底禁用账号,必须用
DROP USER 'u'@'%'或ALTER USER 'u'@'%' ACCOUNT LOCK -
SHOW GRANTS FOR 'u'@'%'可能返回空结果,但这不代表“没权限”,只是没显式授过其他权限 - 别尝试
REVOKE USAGE ON *.* FROM 'u'@'%'——MySQL 会忽略这条命令,不报错也不生效
主机名(Host)不匹配导致撤销失败
MySQL 把 'u'@'localhost' 和 'u'@'%' 视为两个完全不同的账号。撤销时漏掉 Host 或写错,等于对另一个账号操作。
- 先查真实账号:
SELECT User, Host FROM mysql.user WHERE User = 'u'; - 撤销必须带全 Host:
REVOKE SELECT ON mydb.t1 FROM 'u'@'10.20.%.%',不能只写'u'@'%' - 通配符
%不支持模糊匹配——'u'@'%'不等于'u'@'192.168.%',也不能靠它批量撤销多个 Host
真正干净地收回权限,核心不是命令写得多漂亮,而是得先看清权限是怎么来的、当前连接是什么状态、账号全貌是否准确——这些细节比语法本身更容易决定成败。


















