mysql 8.0角色管理需三步:create role→grant权限给角色→grant角色给用户并set default role激活;否则角色无效,用户权限为空。

全局权限(*.*)只该给DBA,别误授给应用账号
全局权限不是“方便管理”的捷径,而是高危开关。一旦执行 GRANT SELECT ON *.*,用户就能读取所有库——包括 mysql、performance_schema、sys 这些系统库,可能意外暴露密码哈希、查询日志或用户定义函数逻辑。
更隐蔽的问题是:哪怕你后续在 mydb 上 REVOKE SELECT,只要 *.* 权限还存在,该用户依然能查 mydb ——因为权限检查顺序是全局 → 数据库 → 表,高优先级未被显式拒绝就直接放行。
-
CREATE DATABASE、DROP DATABASE、SHOW DATABASES必须靠全局权限,数据库级GRANT CREATE ON mydb.*完全无效 - 备份工具如
mysqldump --all-databases要求SELECT+LOCK TABLES全局权限,只给目标库权限会中途报错Access denied for user ... to database 'otherdb' - MySQL 8.0 默认认证插件为
caching_sha2_password,若用旧客户端连带全局权限的账号,可能因插件不兼容直接拒绝连接,而非报权限错误
数据库级权限(mydb.*)才是应用账号的起点
绝大多数业务账号不该碰全局权限。新建应用用户后,第一句授权应是明确范围的数据库级语句,比如 GRANT SELECT, INSERT, UPDATE ON app_prod.* TO 'app_user'@'192.168.1.%'。
这里的关键不是“授了哪些操作”,而是“没授什么”:默认不包含 DELETE、DROP、ALTER,也天然隔离了其他库。即使未来新增 reporting_db,该用户也无权访问,无需额外干预。
- 敏感表需单独收紧:先授库级权限,再用
REVOKE UPDATE ON app_prod.user_auth FROM 'app_user'@'192.168.1.%'剥离关键字段写权限 - MySQL 8.0 要求必须先
CREATE USER,不能像 5.7 那样在GRANT中隐式创建;漏掉这步会报错ERROR 1410 (42000): You are not allowed to create a user with GRANT - 权限变更后,
FLUSH PRIVILEGES在 8.0 中非必需(权限表实时加载),但若曾手动修改过mysql.user等系统表,仍需执行以强制刷新内存缓存
用角色(ROLE)替代重复 GRANT,避免权限漂移
直接对用户授予权限,时间一长就会失控:开发离职、新服务上线、临时排查需求……每次增删都得翻多条 GRANT 语句。MySQL 8.0 的 ROLE 是解决这个问题的正解,本质是权限包的抽象。
比如建一个 read_only_role,集中授予 SELECT 相关权限;再建 dev_role 继承它并加 INSERT、UPDATE。人员变动时,只需 GRANT dev_role TO 'zhangsan'@'%' 或 REVOKE dev_role FROM 'lisi'@'%',权限状态始终可追溯。
- 角色本身不可登录,必须配合
SET DEFAULT ROLE dev_role FOR 'zhangsan'@'%'才生效,否则用户连接后权限为空 - 动态权限(如
BACKUP_ADMIN)不会出现在SHOW GRANTS FOR 'u'@'h'输出里,得用SHOW GRANTS FOR 'u'@'h' USING dev_role查看完整权限集 - 禁止对角色授
PROXY权限,否则可能绕过账户锁定或双因素认证链
权限生效不靠“猜”,而靠 SHOW GRANTS FOR CURRENT_USER()
调试权限问题最常犯的错,就是查错对象:CURRENT_USER() 返回的是认证时匹配的 'user'@'host',而 USER() 返回的是连接时声明的用户名——两者可能完全不同。权限实际生效的是前者。
例如用户用 mysql -u app_user -h 192.168.1.100 连接,但 mysql.user 表里只有 'app_user'@'192.168.1.%' 这条记录,此时 CURRENT_USER() 就是后者,SHOW GRANTS FOR CURRENT_USER() 才反映真实权限;若查 SHOW GRANTS FOR 'app_user'@'localhost',结果完全无关。
- 权限错误往往不报
Access denied,而是抛出看似不相关的错误,比如ERROR 1449 (HY000): The user specified as a definer ('sys'@'localhost') does not exist——这其实是视图/函数定义者账号被删,不是当前用户缺权限 - 列级权限(如
GRANT SELECT(col1,col2) ON db.t TO 'u'@'h')会让SELECT *报错,但SELECT col1,col2可执行;这种细粒度限制容易被忽略,尤其在 ORM 自动生成查询时
权限层级管理真正的复杂点不在语法,而在于“谁在什么时候需要什么最小动作”。全局权限一旦放开,回收成本远高于初始收紧;角色若不设默认启用,等于白建;而查权限时若搞混 CURRENT_USER() 和 USER(),调试就永远在错误方向上打转。











