error 1045报错主因是只读账号缺失usage权限,必须显式执行grant usage on . to 'ro_ops'@'192.168.10.%'才能登录,select等操作权限不包含连接权。

只读账号必须显式授予 USAGE 才能登录
很多人执行完 GRANT SELECT ON mydb.* TO 'ro_ops'@'192.168.10.%' 就去测试登录,结果报错:ERROR 1045 (28000): Access denied for user 'ro_ops'@'192.168.10.50'。这不是密码问题,而是漏了连接权限——SELECT 是操作权,USAGE 才是登录权。MySQL 不会自动给新用户配 USAGE,必须手动补上:
CREATE USER 'ro_ops'@'192.168.10.%' IDENTIFIED BY 'StrongPass!2026';-
GRANT USAGE ON *.* TO 'ro_ops'@'192.168.10.%';(这步不能省) GRANT SELECT ON `myapp_db`.* TO 'ro_ops'@'192.168.10.%';-
GRANT SELECT ON INFORMATION_SCHEMA.* TO 'ro_ops'@'192.168.10.%';(否则SHOW CREATE TABLE失败)
生产环境严禁用 GRANT SELECT ON *.*
这条语句看似方便,实则等同于开放所有库的读取权限,包括 mysql、sys、performance_schema。攻击者或误操作者可直接查 mysql.user 看哈希密码,或从 sys.schema_table_statistics 推断业务热点表。正确做法是按运维职责精确授权:
- 只查业务数据?→
GRANT SELECT ON `orders_db`.*+GRANT SELECT ON `users_db`.* - 需要看慢查询日志?→ 单独加
GRANT SELECT ON performance_schema.events_statements_summary_by_digest(MySQL 8.0+) - 要查复制状态?→
GRANT SELECT ON performance_schema.replication_connection_status,而非整个performance_schema - 绝对不要给
SHOW VIEW、LOCK TABLES、PROCESS——它们不是只读必需项,反而可能被用于探测或阻塞
FLUSH PRIVILEGES 在 MySQL 5.7+ 可省略,但建议保留
官方文档说 GRANT 会自动刷新权限缓存,所以多数情况下不执行 FLUSH PRIVILEGES 也能立即生效。但生产环境存在两个例外:
- 你曾手动修改过
mysql.user表(比如用UPDATE直接改认证插件),这时必须执行FLUSH PRIVILEGES否则变更不加载 - 某些中间件(如 ProxySQL、MaxScale)依赖
FLUSH PRIVILEGES触发权限同步,跳过会导致连接后权限不一致 - 运维脚本里统一加上这句,比事后排查“为什么刚授的权不生效”更省时间
验证时别只测 SELECT,重点看边界行为是否真被拦住
登录后立刻跑这几条命令,确认权限没“漏”:
-
INSERT INTO myapp_db.users VALUES (999, 'test');→ 必须报ERROR 1142 (42000): INSERT command denied -
SELECT * FROM mysql.user LIMIT 1;→ 应报错,除非你明确授了mysql库 -
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'myapp_db' LIMIT 1;→ 应成功(这是元数据只读,安全) -
SELECT * FROM performance_schema.threads LIMIT 1;→ 应失败(默认不开放,除非你显式授权)
最容易被忽略的是视图和函数调用:如果业务库用了 SQL SECURITY DEFINER 的视图,只读账号可能绕过表级限制。务必检查所有视图定义,改成 SQL SECURITY INVOKER。











