MySQL 8.0角色需显式激活才能生效:SET ROLE 'role' 或设DEFAULT ROLE,否则权限不触发;SHOW GRANTS不显示角色权限,须用USING子句查看;mandatory_roles会强制绑定且不可撤销。

角色没激活,权限就挂在那里不动
MySQL 8.0 的角色不是“授完即用”,而是像一把锁住的保险箱:GRANT 给角色、GRANT 角色给用户,只是把钥匙配好了、交到用户手上,但没拧开锁——CURRENT_ROLE() 返回 NULL 就说明锁还闭着。
常见错误现象:SELECT * FROM sales.orders; 报 ERROR 1142 (42000): SELECT command denied,而 SHOW GRANTS FOR 'dev_user'@'%' 看起来一切正常。
- 临时激活当前会话:
SET ROLE 'analyst';(必须显式指定角色名,不能只写SET ROLE) - 设为默认角色(推荐):
ALTER USER 'dev_user'@'%' DEFAULT ROLE 'analyst';,下次登录自动启用 - 全局开关慎用:
SET PERSIST activate_all_roles_on_login = ON;,影响所有新连接,重启后仍生效
注意:SET ROLE 只对当前连接有效;DBeaver/Navicat 等工具复用连接池,断开重连后需重新激活。
SHOW GRANTS 看不到角色权限,不等于没授权
SHOW GRANTS FOR 'dev_user'@'%' 默认只显示直接授予用户的权限,不会展开角色所含权限——这是设计行为,不是 bug。
真正要查角色带来的权限,得加 USING 子句:SHOW GRANTS FOR 'dev_user'@'%' USING 'analyst';
- 前提是该角色已绑定(
GRANT 'analyst' TO 'dev_user'@'%')且当前会话已激活(CURRENT_ROLE()返回'analyst') - 如果
USING查询返回空,先检查绑定关系:SELECT * FROM mysql.role_edges WHERE to_user = 'dev_user' AND to_host = '%'; - 角色名必须带主机部分,
'analyst'@'%'和'analyst'是两个不同角色
角色权限只在匹配的库和上下文中生效
角色授的是 sales.*,但用户执行 USE otherdb; SELECT * FROM orders;,照样报错——角色权限不跨库自动继承。
容易被忽略的细节:
- 执行前确认当前数据库:
SELECT DATABASE();,返回NULL表示未选库,任何表操作都会失败 - 角色权限中的库名、表名、主机名全部区分大小写且严格匹配;
GRANT SELECT ON Sales.*≠sales.* - 函数权限(
FUNCTION)会被 MySQL 自动转成过程权限(PROCEDURE),导致实际执行时权限不匹配
mandatory_roles 配置会让角色“隐形强制”且无法删除
如果在 my.cnf 里写了 mandatory_roles = 'auditor'@'%',或执行过 SET PERSIST mandatory_roles = '''auditor''@''%''',这个角色就会变成所有用户登录时自动获得的“影子权限”。
表现很隐蔽:
-
SHOW GRANTS FOR 'dev_user'@'%'不显示它,但CURRENT_ROLE()会包含'auditor'@'%' -
REVOKE 'auditor'@'%' FROM 'dev_user'@'%'或DROP ROLE 'auditor'@'%'全部失败 - 清空只能用:
SET PERSIST mandatory_roles = '';,已有连接不受影响,仅新连接生效
真正卡住人的地方从来不是建角色或赋权,而是“激活”这个隐式环节——它不报错、不提示、不写日志,只静静让权限失效。别只信 SHOW GRANTS,一定要跑一次 CURRENT_ROLE()。


















