MySQL触发器无法获取真实业务用户权限上下文,只能通过应用层显式传入会话变量(如@current_user_id)并在触发器中用IFNULL兜底,否则日志全为unknown;所有依赖CURRENT_USER()做权限拦截的逻辑均会失效。

MySQL触发器里根本拿不到真实执行者权限上下文,所有试图在触发器内做角色判断、租户隔离或权限拦截的逻辑,都会失效或误判。这不是配置问题,是设计限制——触发器运行时只认 CURRENT_USER(),而它返回的是数据库连接账户,不是业务系统里的用户ID或角色。
为什么CURRENT_USER()不能当操作人用
应用通常用连接池统一以 app@10.0.1.5 连接数据库,CURRENT_USER() 就永远是这个值。即使你登录时用的是 admin@localhost,只要应用没换账号,触发器里查出来的就是连接池账号。更关键的是:
- 触发器内禁止执行
SELECT查询系统表(如mysql.user或information_schema.role_table_grants),连验证角色都做不到 - MySQL 8.0 的 RLS(行级安全)虽支持
current_role,但触发器不继承 RLS 上下文,策略表达式在触发器里不生效 - SQL Server 的
IS_MEMBER()、PostgreSQL 的pg_has_role()在各自触发器中也常返回NULL或默认角色,不可靠
唯一可行的传参方式:会话变量 + IFNULL兜底
必须由应用层在每次 DML 前显式设置会话变量,并在触发器中直接引用。漏设或设错会导致日志全为 unknown,且无法事后补救:
- 应用执行 SQL 前发:
SET @current_user_id = 'u_12345'; SET @current_role = 'editor'; - 触发器中写:
NEW.audit_user_id := IFNULL(@current_user_id, 'unknown');(注意:不能写SELECT @current_user_id,触发器内禁查) - 变量作用域仅限当前连接,连接池场景下每条业务 SQL 都要重设,否则可能残留上个用户的值
- DBA 直连调试或脚本导入时极易漏设,所以
IFNULL(, 'unknown')是硬性要求,不能省
跨库写入日志时,DEFINER权限和log_bin_trust_function_creators必须同时满足
如果触发器要往 audit_db.log_table 写记录,除了会话变量,还涉及两层权限校验:
-
DEFINER用户必须真实存在,且对audit_db.log_table有INSERT权限(GRANT INSERT ON audit_db.log_table TO 'definer_user'@'%') - 若 MySQL 开启了 binlog(5.7+ 默认开启),则必须设置
log_bin_trust_function_creators = 1,否则触发器执行时直接报ERROR 1419 - 云数据库(如阿里云 RDS)禁用
SUPER,这条路走不通,只能靠该参数持久化配置 -
SHOW CREATE TRIGGER必须检查输出中的DEFINER=`xxx`@`yyy`,不能依赖 GUI 工具自动生成的CURRENT_USER()
真正容易被忽略的是:所有绕过 SQL 层的变更——mysqldump --no-create-info 导入、LOAD DATA INFILE、中间件批量写入、甚至 MySQL Router 的透传流量——触发器一概不触发。权限校验若只押注触发器,等于在关键路径上留了个永久后门。


















