必须显式执行 SET DEFAULT ROLE 且 host 精确匹配才能使 CURRENT_ROLE() 返回非 NONE,否则始终为 NONE;常见失效原因包括 activate_all_roles_on_login=ON、账户锁定、角色无权限、特殊字符未加反引号、连接池复用连接等。

必须显式执行 SET DEFAULT ROLE,且用户已通过 GRANT role TO user 被授予该角色,否则登录后 CURRENT_ROLE() 永远返回 NONE。
SET DEFAULT ROLE 语句必须满足 host 精确匹配
MySQL 角色绑定和默认设置都严格校验 user@host 的完整字符串。比如:
- 你用
GRANT 'app_reader' TO 'alice'@'localhost'授予了角色 - 就必须用
SET DEFAULT ROLE 'app_reader' TO 'alice'@'localhost',不能写成'alice'@'%'或省略@'localhost' - 若用户实际连接时 host 解析为
127.0.0.1(而非localhost),那即使授了角色也查不到匹配项,SET DEFAULT ROLE会报ERROR 3530
验证方式:查 mysql.role_edges 表确认是否存在对应记录:
SELECT * FROM mysql.role_edges WHERE TO_USER = 'alice' AND TO_HOST = 'localhost';
SET DEFAULT ROLE 不生效的常见原因
执行成功不等于权限就可用。以下情况会导致登录后仍无权限:
-
activate_all_roles_on_login=ON已启用 → 此时SET DEFAULT ROLE完全被忽略,所有已授予角色都会自动激活 - 用户账户被锁(
account_locked='Y')或密码过期(password_expired='Y'),系统跳过默认角色加载 - 角色本身没被授过任何权限,比如只执行了
CREATE ROLE 'r1',但没跑GRANT SELECT ON db.* TO 'r1' - 使用了带特殊字符的角色名(如
ci-pipeline),却没加反引号:SET DEFAULT ROLE `ci-pipeline` TO 'u'@'%';
连接池场景下默认角色常被绕过
HikariCP、Druid 等连接池默认复用连接,新获取的连接不会重新触发默认角色激活逻辑——哪怕你设了 SET DEFAULT ROLE,只要连接不是全新建立,CURRENT_ROLE() 就可能还是 NONE。
- 最稳妥方案是在 JDBC URL 中加初始化参数:
sessionVariables=default_role=app_reader@% - 或者配置
connection-init-sql="SET ROLE 'app_reader'"(注意:此为会话级,非持久化) - 不要依赖“设一次就永远生效”的假设,连接池环境里每次取连接都要视为独立上下文
查看和验证是否真生效
别只信 SHOW GRANTS FOR 'u'@'h',它默认不显示角色继承的权限:
- 查当前会话活跃角色:
SELECT CURRENT_ROLE();—— 必须返回非NONE值 - 查用户通过某角色获得的权限:
SHOW GRANTS FOR 'u'@'h' USING 'app_reader'@'%'; - 查角色自身权限:
SHOW GRANTS FOR 'app_reader'@'%'; - 登录后直接执行一条受控 SQL(如
SELECT 1 FROM app.t LIMIT 1)比任何元数据查询都可靠
最容易被忽略的是:角色权限在连接建立瞬间决定,之后不会随 SET DEFAULT ROLE 变更而动态更新;改完必须重连,且连接池要清空旧连接。











