MySQL触发器中AES_ENCRYPT()需用UNHEX()转换密钥,字段类型须为VARBINARY/BLOB,仅BEFORE INSERT可赋值NEW字段;加密后无法索引查询,应辅以哈希或确定性加密。

触发器里不能直接用 AES_ENCRYPT() 加密敏感字段
MySQL 的 AES_ENCRYPT() 要求密钥是二进制或十六进制格式,而触发器里传字符串密钥容易被截断或编码错——比如你写 AES_ENCRYPT(new.ssn, 'mykey123'),实际加密结果不稳定,甚至返回 NULL。
常见错误现象:INSERT 成功但加密字段存的是 NULL 或乱码;用相同密钥在 SELECT 里调 AES_DECRYPT() 解不出来。
- 必须把密钥转成
UNHEX()格式,例如AES_ENCRYPT(new.ssn, UNHEX('6D796B6579313233')) - 更稳妥的做法是把密钥存在系统变量或配置表里,避免硬编码;否则改密钥就得重写触发器
-
AES_ENCRYPT()返回VARBINARY,目标字段类型必须设为VARBINARY(255)或BLOB,不能是VARCHAR
BEFORE INSERT 触发器才能修改 new. 字段值
只有 BEFORE INSERT 触发器允许赋值给 NEW.column_name;AFTER INSERT 里改 NEW 没效果,还可能报错 Can't update table 'xxx' in stored function/trigger。
使用场景:你希望插入明文手机号,自动存为密文到数据库,且应用层无感——这就必须靠 BEFORE INSERT 拦截并替换。
- 触发器体里写
SET NEW.phone = AES_ENCRYPT(NEW.phone, UNHEX('...')); - 如果字段可能为
NULL,得先判断:IF NEW.phone IS NOT NULL THEN ... END IF; - 别在触发器里做复杂逻辑(比如查另一张表取动态密钥),会拖慢写入、引发死锁
加密后字段无法走索引,WHERE 条件要改写
加密后的值是随机二进制串,WHERE phone = '138...' 这种查询完全失效——不是性能差,是根本查不到。
真实需求往往是“查某个用户”,但又不想暴露明文。这时候得换思路:
- 保留一个不可逆的哈希字段(如
phone_hash CHAR(64) AS (SHA2(phone, 256)) STORED)用于等值查询 - 或者加一个带盐的确定性加密(如
AES_ENCRYPT(phone, UNHEX(salt_key))),但盐必须固定且保密 - 千万别为了能查,在触发器里同时存明文+密文字段——等于白加密
PostgreSQL 用 pgcrypto 时注意函数签名差异
PostgreSQL 没有内置 AES_ENCRYPT(),得装 pgcrypto 扩展,而且函数名和参数顺序不一样:encrypt(data, key, type),不是 MySQL 那套。
容易踩的坑:直接照搬 MySQL 写法,比如 encrypt(new.email, 'key', 'aes') 报错,因为 key 必须是 bytea 类型。
- 正确写法:
encrypt(new.email::bytea, decode('6d796b6579313233', 'hex'), 'aes') -
decode()和encode()是关键,漏掉就加密失败 - PostgreSQL 触发器函数必须用
LANGUAGE plpgsql,且需显式RETURN NEW;

















