真正只读必须显式授予select和usage权限、逐项revoke写权限(含lock tables/super)、补information_schema.tables授权,并用新连接验证select成功而insert及跨库访问均报错。

GRANT SELECT 之后必须显式 REVOKE 写权限
只执行 GRANT SELECT ON `db_name`.* TO 'user'@'host'; 并不能保证真正只读。MySQL 权限是叠加的,如果用户之前被授过 ALL PRIVILEGES、INSERT 或其他写权限,这些权限不会被新 GRANT SELECT 覆盖或清除。
- 先查清残留权限:
SHOW GRANTS FOR 'user'@'host';,重点看输出里是否含INSERT、UPDATE、DELETE、DROP、ALTER、LOCK TABLES、EXECUTE、TRUNCATE、CREATE TEMPORARY TABLES - 逐项撤销(关键步骤):
REVOKE INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, INDEX, LOCK TABLES, EXECUTE, TRUNCATE, CREATE TEMPORARY TABLES ON `db_name`.* FROM 'user'@'host'; - 特别注意
SUPER:它能绕过所有权限检查,必须单独查并撤销:SELECT Super_priv FROM mysql.user WHERE User = 'user' AND Host = 'host';,若为Y,执行REVOKE SUPER ON *.* FROM 'user'@'host';
MySQL 8.0+ 必须显式授予 USAGE 权限
在 MySQL 8.0 及以后版本,仅授 SELECT 会导致用户根本无法登录,报错 ERROR 1045 (28000): Access denied——这不是密码问题,而是缺少连接基础权限。
-
USAGE ON *.*是空权限占位符,只负责认证通过后维持连接,不赋予任何操作能力 - 必须显式执行:
GRANT USAGE ON *.* TO 'user'@'host';(可与GRANT SELECT分开执行,顺序无关) - 别依赖“自动附带”,8.0+ 已移除该行为;漏掉这步,后续所有授权都无效
SHOW CREATE TABLE 失败?大概率缺 information_schema 授权
很多监控工具、ORM 或 DBA 手动查表结构时会执行 SHOW CREATE TABLE t1,但它底层依赖 information_schema.TABLES 和 information_schema.COLUMNS 查询。只授业务库权限时,该命令会报 ERROR 1142 (42000): SELECT command denied。
- 补授权即可(MySQL 8.0+):
GRANT SELECT ON `information_schema`.`TABLES` TO 'user'@'host'; - 如还需字段注释、默认值等完整元数据,加:
GRANT SELECT ON `information_schema`.`COLUMNS` TO 'user'@'host'; - 不需要额外授
mysql或performance_schema——除非明确需要查用户/变量等系统信息
验证时必须用新连接,且测试跨库访问
旧连接会缓存权限快照,即使你已 FLUSH PRIVILEGES,也不会立即生效。验证必须走全新会话,并覆盖边界场景。
- 用新终端登录:
mysql -u user -p -h host,不要复用已有 shell - 成功案例:
SELECT COUNT(*) FROM db_name.users; - 失败案例:
INSERT INTO db_name.users(name) VALUES('x');→ 应报ERROR 1142 - 关键隔离验证:
SELECT * FROM other_db.any_table;→ 也必须报错,否则说明权限没限定到库级 - 别只信
SHOW GRANTS输出,它不反映隐式继承或残留权限
实际配置中最容易被跳过的,是 USAGE 授权和 information_schema 补权这两步——它们不直接关联“读数据”,但缺一不可。尤其在自动化脚本或 CI/CD 流程中,漏掉就会导致应用连不上或元数据查询崩掉。











