thinkphp中加密字段应通过模型的getattr/setattr修改器实现自动加解密,写入时加密、读取时解密,仅对指定字段生效,避免使用数据库函数或事件回调;需判空防重复加密,用可逆算法,配合hidden属性防止密文泄露。

加密字段在模型里怎么自动加解密
ThinkPHP 的 getAttr 和 setAttr 修改器是处理加密字段最直接的方式,不是靠中间件或全局钩子——那些容易漏掉 CLI 场景或关联查询。核心逻辑是:写入时用 setAttr 加密,读取时用 getAttr 解密,且只对指定字段生效。
常见错误是把加密逻辑塞进 save() 方法或事件回调里,结果 where()->update() 或批量赋值时完全不触发;或者在修改器里调用 encrypt() 却没判空,导致 null 变成一串乱码密文。
- 加密必须用可逆算法(如
openssl_encrypt),不能用md5或password_hash——后者无法还原,字段就废了 -
setAttr里要判断原始值是否已加密(比如检查是否为 base64 编码的密文),避免重复加密 - 解密失败时返回
null或抛异常,别静默吞掉错误,否则查出来全是空值还不好定位 - 示例:
protected function setPasswordAttr($value) { return encrypt($value, config('app.crypt_key')); } protected function getPasswordAttr($value) { return decrypt($value, config('app.crypt_key')); }
为什么用 hidden 属性 + 修改器比数据库函数更可靠
有人想用 MySQL 的 AES_ENCRYPT() 直接在 SQL 层加解密,但这样会导致 ORM 失效:查询条件写 where('password', '123') 实际查的是明文,而数据库存的是密文,永远不匹配;而且迁移、备份、CLI 脚本、跨库同步全会出问题。
ThinkPHP 模型的 hidden 属性配合修改器才是正解——它让加密/解密逻辑收口在模型层,不影响 SQL 构建,也不污染数据库抽象。
-
hidden里声明加密字段(如['password']),防止序列化时意外暴露密文 - 修改器只在模型属性访问时生效,
Db::table()原生查询不受影响,该绕过就绕过 - 注意:
hidden不影响toArray()的输出,如果要彻底过滤,得重写toArray()或用visible - 兼容性上,5.1 和 6.x 都支持,但 6.x 的修改器语法统一为
get{Field}Attr/set{Field}Attr,别混用旧版_get前缀
where() 查询加密字段时怎么写条件
不能直接写 where('password', $input),因为数据库里存的是密文,而 $input 是明文。必须手动加密后比对,且加密逻辑要和 setAttr 完全一致,包括密钥、向量、填充方式。
典型翻车点是开发环境用一个密钥,生产环境换了一个,或者测试时用了硬编码密钥,上线却读配置失败,导致所有登录都报错。
- 推荐封装一个静态方法,比如
UserModel::encryptPassword($plain),所有地方复用 - 查询时用
where('password', UserModel::encryptPassword($input)),别在闭包里现场调用encrypt() - 如果字段要支持模糊搜索(比如后台查“张*”),加密就不可行——得改用非对称加密+索引字段,或放弃加密
- 注意:
findOrEmpty()这类快捷方法照样走修改器,但whereRaw()不走,慎用
加密字段和关联查询、分页、缓存怎么共存
关联模型里如果有加密字段,修改器默认不触发——ThinkPHP 不会自动递归处理关联对象的属性。分页的 toArray() 也不触发修改器,除非你显式调用 each() 或遍历后取属性。
最容易被忽略的是缓存:如果用 cache(true) 缓存整个模型,加密字段会以密文形式缓存;下次读缓存再走 getAttr,就变成“密文 → 解密 → null”,因为解密函数收到的是已经解过的数据。
- 关联查询中需手动调用
$user->profile->getAttr('phone')触发解密,不能依赖自动转换 - 分页结果要用
collection->each(function ($item) { $item->getAttr('id_card'); })显式触发,或改用hidden+ API 层手动处理 - 缓存前先
toArray(),并确保加密字段已通过getAttr解密;或者缓存时不包含加密字段,API 返回时再单独查一次 - 性能上,每个加密字段每次访问都加解密,高频读场景建议加内存缓存(如
think\Cache)解密结果,键用"decrypt_{$field}_{$value}"
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











