MySQL 8.0+支持角色管理:先CREATE ROLE建角色,再GRANT授权给角色,最后GRANT角色给用户并SET DEFAULT ROLE启用;SHOW GRANTS不显示角色权限,需USING或查INFORMATION_SCHEMA;禁用WITH GRANT OPTION和匿名用户;连接池需配置sessionVariables或connection-init-sql确保角色正确激活。

MySQL里怎么创建角色并授权
角色不是MySQL 5.7原生支持的,得用8.0+。低于这个版本,CREATE ROLE会直接报错 ERROR 1064。确认版本后,先建角色,再把权限批量赋给角色,最后把角色授给用户——这是RBAC落地最稳的顺序。
- 建角色:
CREATE ROLE 'app_reader'、CREATE ROLE 'app_writer' - 授权限(对库):
GRANT SELECT ON mydb.* TO 'app_reader';GRANT INSERT, UPDATE ON mydb.orders TO 'app_writer'(注意:别直接GRANT ALL到库级,太宽) - 把角色给用户:
GRANT 'app_reader' TO 'webapp@'10.20.30.%'(主机段要具体,别用'%') - 启用角色(用户登录后默认不激活):
SET DEFAULT ROLE 'app_reader' TO 'webapp'@'10.20.30.%',否则权限不生效
为什么SHOW GRANTS看不到角色权限
执行 SHOW GRANTS FOR 'webapp'@'10.20.30.%' 只显示直授权限,不显示通过角色继承的权限——这是MySQL的设计行为,不是漏了。想查全量有效权限,得用 SHOW GRANTS FOR 'webapp'@'10.20.30.%' USING 'app_reader',或者登录后运行 SELECT * FROM INFORMATION_SCHEMA.ROLE_TABLE_GRANTS。
- 角色权限只在激活后才生效,
SELECT CURRENT_ROLE()能看当前激活了哪些角色 - 如果用户有多个角色但没设默认,每次连接都要手动
SET ROLE 'app_reader',线上应用容易忘,建议提前配好DEFAULT ROLE -
mysql.role_edges表记录角色分配关系,但它是系统表,普通账号查不到,得用root或有SELECT ON mysql.*的账号
生产环境禁用WITH GRANT OPTION和匿名用户
任何带 WITH GRANT OPTION 的授权,等于给了用户“转授权限”的能力,一旦被滥用,最小权限就彻底失效。匿名用户(''@'localhost')更是高危,默认允许无密码登录,必须删。
- 检查并回收:用
SELECT User,Host,Grant_priv FROM mysql.user WHERE Grant_priv='Y'找出所有能转授的账号,逐个REVOKE GRANT OPTION - 删匿名用户:
DROP USER ''@'localhost'(注意引号是空字符串) - 禁止新用户带
GRANT OPTION:自动化部署脚本里,所有GRANT语句结尾坚决不加WITH GRANT OPTION - 权限变更后记得
FLUSH PRIVILEGES,但角色相关操作(如CREATE ROLE)不需要刷新
应用连接池如何适配角色激活
很多Java连接池(比如HikariCP)默认复用连接,而MySQL角色是会话级的——如果一个连接被A用户用过又归还池中,B用户取到后可能还带着A的活跃角色,导致越权或权限丢失。
- 解决方案一:连接初始化时强制重置角色,配置
connection-init-sql=SET ROLE NONE,再由应用按需SET ROLE - 解决方案二:连接串加
?sessionVariables=default_role=app_reader(MySQL 8.0.19+ 支持),让每次新建连接自动激活指定角色 - 千万别依赖应用代码里写
SET ROLE后就不管了——连接池可能复用旧连接,角色状态不会自动同步 - 验证方法:从连接池取一个连接,执行
SELECT CURRENT_ROLE(), USER(),看是否匹配预期
角色名、主机段、默认角色设置这三处最容易写错,改完一定要用目标用户实际连一次,跑 SELECT * FROM INFORMATION_SCHEMA.ENABLED_ROLES 确认生效。权限不是配完就完事,得进得去、查得对、写不了不该写的——这才是最小权限的真实状态。


















