应统一在事务闭包内使用crypt::encryptstring与crypt::decryptstring,禁用模型访问器/修改器,采用临时明文字段异步加密,并锁定app_key一致性,以确保敏感字段加解密在事务中读写一致。

Crypt::encryptString() 和 Crypt::decryptString() 是最安全、最可控的起点,别一上来就往模型里塞 $casts = ['field' => 'encrypted']——除非你真在 Laravel 9+ 且确认字段类型、密钥生命周期、查询限制全盘可控。
数据库字段类型必须改,否则加密值直接被截断
加密后是带 IV 和 MAC 的 base64 字符串,空字符串加密后也约 144 字符;VARCHAR(255) 看似够用,但实际可能被 MySQL 静默截断(尤其开启严格模式前)。
常见错误现象:decrypt() 报 DecryptException,但查数据库发现字段值明显变短。
正确做法:
- 迁移中明确写
$table->text('api_key_encrypted'),不是string() - MySQL 推荐
TEXT或MEDIUMTEXT;SQLite 优先用BLOB,TEXT也可但别用CHAR - 已有表务必执行
ALTER TABLE users MODIFY api_key_encrypted TEXT,别只改迁移文件
用 encryptString() 而不是 encrypt() 存纯字符串
Crypt::encrypt() 会先序列化输入(哪怕只是字符串),再加密;Crypt::encryptString() 直接加密原始字符串,无额外序列化开销,解密后就是干净字符串。
使用场景:
PHP中文网提供Laravel 13.2.0版本下载,Laravel框架 是基于 PHP 8.3+ 的高性能框架,官方推荐通过 Composer 安装。它内置 AI SDK、JSON:API Resources 及原生向量搜索,支持属性驱动开发与队列路由,大幅提升开发效率。相比旧版,13.2.0 优化了缓存 TTL 管理与实时通信,无需 Redis 即可横向扩展。作为现代 Web 开发首选,它兼顾安全与极速体验,助您快速构建企业级应用。
- 存 API token、密钥、手机号等简单字符串时,无条件选
encryptString() - 存数组或对象(如用户偏好配置)才用
encrypt() - 若误用
encrypt()存字符串,解密后得到的是字符串对象而非原生 string,可能引发json_encode()或比较异常
use Illuminate\Support\Facades\Crypt;<br>$encrypted = Crypt::encryptString('sk_live_abc123'); // ✅<br>// $decrypted 是 'sk_live_abc123',类型 string<br>$decrypted = Crypt::decryptString($encrypted);
解密必须 try-catch,且不能假设失败只是“密钥不对”
Crypt::decryptString() 失败时抛出 Illuminate\Contracts\Encryption\DecryptException,但原因不止 APP_KEY 变更:
- 缓存污染:Redis 里混入了非加密值
- 字段被手动更新为明文(运维误操作)
- 数据库字符集不兼容(如 latin1 存了 utf8 base64)
- APP_KEY 泄露后被篡改过密文
- 所有
decryptString()调用都包在 try-catch 里 - catch 块中记录日志(含字段名、ID、原始密文前 20 字符),但绝不打全密文
- 生产环境避免在访问器(
get***Attribute)里调用 decrypt —— 一次toArray()可能触发 N 次解密 + N 次异常捕获
$casts = ['field' => 'encrypted'] 看似方便,实则绕不开四个硬约束
Laravel 9+ 内置的 encrypted cast 确实省去手动加解密,但它不是“开关”,而是把问题封装进框架机制里:
- 仍要求字段是
TEXT类型,否则存不进、取不出 - 仍依赖 APP_KEY 不变;换 key 后存量数据全部失效,无迁移钩子
-
where('api_token', $token)永远查不到——因为数据库里存的是密文,不是明文 - 解密失败时返回
null,而不是抛异常,容易掩盖数据异常(比如某条记录被人工改过)










