MySQL 8.0+角色功能需显式创建、绑定并激活三步缺一不可;版本低于8.0会报ERROR 1064;角色名禁用反引号且不能与用户名冲突;GRANT仅绑定不激活,须执行SET DEFAULT ROLE;SHOW GRANTS不显示角色权限,需USING或查系统表;禁用WITH GRANT OPTION和匿名用户保障RBAC安全。

MySQL 8.0+ 的 ROLE 功能不是“开了就能用”,必须显式创建、绑定、激活三步全做对,缺一不可;否则用户连 SELECT 都会报 Access denied。
CREATE ROLE 报 ERROR 1064 怎么办
这是最常踩的第一个坑:你正在用 MySQL 5.7 或更低版本。角色功能是 8.0.0 才引入的,低于该版本执行 CREATE ROLE 必然触发语法错误 ERROR 1064 (42000)。
- 先确认版本:
SELECT VERSION();—— 输出必须是8.0.x或更高 - 如果版本正确但仍报错,检查当前用户是否拥有
CREATE ROLE权限(普通 DBA 用户默认没有) - 别用反引号包角色名写成
CREATE ROLE `app_reader`,MySQL 角色名支持单引号或不加引号,但反引号在部分客户端会引发解析歧义 - 角色名不能与现有用户名冲突,否则
CREATE ROLE 'admin'会失败(若已有'admin'@'%'用户)
GRANT 权限给角色后,用户还是没权限
因为角色权限不会自动生效——GRANT 'app_reader' TO 'webapp'@'10.20.30.%' 只是建立了“用户→角色”的绑定关系,不等于激活。
- 必须额外执行:
SET DEFAULT ROLE 'app_reader' TO 'webapp'@'10.20.30.%' - 如果跳过这步,用户登录后执行
SELECT CURRENT_ROLE()返回NONE,所有角色权限都不可见 - 连接池场景下更危险:很多 JDBC 连接池(如 HikariCP)默认不执行初始化 SQL,需显式配置
connection-init-sql="SET DEFAULT ROLE 'app_reader'"或sessionVariables=default_role=app_reader - 一个用户可被授予多个角色,但
SET DEFAULT ROLE ALL TO ...会违反最小权限原则,慎用
SHOW GRANTS 看不到角色权限,是不是授权失败了
不是失败,是设计如此。SHOW GRANTS FOR 'webapp'@'10.20.30.%' 默认只显示直接授予用户的权限(比如 USAGE),完全不展开角色继承的权限。
- 要查角色赋予的权限,得用:
SHOW GRANTS FOR 'webapp'@'10.20.30.%' USING 'app_reader' - 或者登录后查系统表:
SELECT * FROM INFORMATION_SCHEMA.ROLE_TABLE_GRANTS WHERE GRANTEE = '''webapp''@''10.20.30.%''' AND ROLE_NAME = 'app_reader'; -
mysql.role_edges表记录角色分配关系,但它是系统表,普通账号无权访问,只有root或有SELECT ON mysql.*的账号能查 - 别依赖
SHOW GRANTS做权限审计,它容易造成“误判为未授权”
为什么生产环境必须禁用 WITH GRANT OPTION 和匿名用户
这两类配置会直接瓦解 RBAC 的最小权限根基。
-
WITH GRANT OPTION允许用户把权限再授给他人——一旦某个应用账号被黑,攻击者就能扩散权限,甚至创建高危账号 - 匿名用户(
''@'localhost')不设密码、不限主机,MySQL 启动时可能自动创建,是典型后门入口 - 检查现存高危账号:
SELECT User,Host,Grant_priv FROM mysql.user WHERE Grant_priv='Y';,对非 DBA 账号逐个REVOKE GRANT OPTION ON *.* FROM ... - 删匿名用户:
DROP USER ''@'localhost';(注意两个单引号是空字符串) - 自动化部署脚本中,所有
GRANT语句结尾严禁出现WITH GRANT OPTION
RBAC 的关键不在“建角色”,而在“每次连接都确保角色激活”。连接池、ORM、定时任务脚本——任何绕过交互式登录的路径,都可能漏掉 SET DEFAULT ROLE,这才是线上权限失效最隐蔽的原因。











