MySQL 8.0角色权限生效必须四步闭环:CREATE ROLE建角色、GRANT授予权限给角色、GRANT将角色分配给用户、SET DEFAULT ROLE激活;缺一不可,否则CURRENT_ROLE()返回NULL,权限为空。

必须显式创建角色、精确授予权限、绑定用户并激活,默认不生效——跳过任一环节,权限就是空的。
CREATE ROLE 和 GRANT 权限给角色不能合并执行
MySQL 8.0 不支持在 GRANT 时隐式创建角色。执行 GRANT SELECT ON sales_db.* TO 'ro_role' 前,必须先运行 CREATE ROLE 'ro_role',否则报错 ERROR 3530 (HY000): Role does not exist。
- 角色名区分大小写,且不能与已有用户名冲突(哪怕
host不同) - 推荐始终加
IF NOT EXISTS:例如CREATE ROLE IF NOT EXISTS 'ro_role',避免脚本重复执行失败 - 角色创建后是空容器,
SHOW GRANTS FOR 'ro_role'会返回空;必须后续用GRANT ... TO 'ro_role'显式赋权 - 角色不支持列级权限:
GRANT SELECT(col1) ON t1 TO 'ro_role'会直接报错
GRANT 角色给用户 ≠ 权限生效,缺 SET DEFAULT ROLE 就等于没配
执行 GRANT 'ro_role' TO 'app_user'@'%' 只是在 mysql.role_edges 表里加一条绑定记录,用户登录后 CURRENT_ROLE() 仍返回 NONE,查询照样被拒绝。
- 必须额外执行
SET DEFAULT ROLE 'ro_role' TO 'app_user'@'%',且host段要完全匹配('%'不能覆盖'localhost') - 若想激活多个角色,写成
SET DEFAULT ROLE 'ro_role', 'log_writer' TO 'app_user'@'%';SET DEFAULT ROLE ALL虽然可行,但容易违反最小权限原则 - 普通用户无权执行
SET DEFAULT ROLE,该操作必须由具有APPLICATION_PASSWORD_ADMIN或更高权限的账号完成
验证权限是否真生效,别只看 SHOW GRANTS FOR
SHOW GRANTS FOR 'app_user'@'%' 默认只显示“谁被授了哪个角色”,不会展开角色内部的权限。这常导致误判“权限已配好”,实际用户连 SELECT 都被拒。
- 查角色带来的真实权限,必须用
SHOW GRANTS FOR 'app_user'@'%' USING 'ro_role' - 确认当前会话是否激活:登录后执行
SELECT CURRENT_ROLE(),返回非NONE值才算成功 - 检查绑定关系是否写入:查
SELECT * FROM mysql.role_edges WHERE to_user = 'app_user' AND to_host = '%' - 旧版客户端(如 MySQL 5.7 客户端连接 8.0 服务端)可能忽略角色信息,建议用
mysql --version ≥ 8.0.11验证
全局权限 (*.*) 是高危开关,不是管理捷径
给角色授 SELECT ON *.* 看似省事,实则把 mysql、information_schema、performance_schema 全部暴露,极易泄露密码哈希、SQL 日志或用户函数逻辑。
- 应用账号起点应是数据库级权限,例如
GRANT SELECT, INSERT ON app_prod.* TO 'ro_role' - 敏感表需单独收紧:先授库级权限,再用
REVOKE UPDATE ON app_prod.user_auth FROM 'ro_role' - 只写角色禁用
SELECT时,要保留必要元数据访问(如GRANT SELECT(id) ON log_db.events TO 'rw_role'),否则INSERT ... ON DUPLICATE KEY UPDATE会失败 -
LOCK TABLES、CREATE TEMPORARY TABLES必须显式禁止,否则只读账号可通过临时表绕过限制
最常被跳过的其实是 activate_all_roles_on_login = ON 这个全局变量——它默认关闭,即使角色全配对、默认也设了,用户登录后依然拿不到权限。这个开关必须写进 my.cnf 并重启,或用 SET GLOBAL 临时开启(注意重启失效)。


















