加密字段必须用encrypt casting,其他方式无法自动解密且破坏类型安全;依赖app_key,更换即丢数据;不支持where查询和索引,仅适用于纯存储敏感字段。

加密字段必须用 encrypt casting,不能靠模型事件或访问器
Laravel 的字段加密只在 Eloquent 属性 cast 机制里原生支持,其他方式(比如在 creating 或 saving 事件里手动加密)不会自动解密读取,也绕过了框架的类型安全处理。用错地方会导致写入加密、读取明文,或者反过来——读出来一堆乱码。
实操建议:
- 在模型中定义
$casts数组,键为字段名,值为'encrypt'字符串 - 确保该字段数据库类型是
TEXT或VARCHAR(足够存 Base64 编码后的密文) - 不要对主键、外键、索引字段滥用加密——它们无法被 WHERE 查询直接匹配
示例:
protected $casts = [
'ssn' => 'encrypt',
'api_token' => 'encrypt'
];
encrypt casting 依赖 Laravel 的 APP_KEY,换密钥等于丢数据
加密/解密全程使用 APP_KEY,不是独立密钥。一旦重置或更换 APP_KEY,所有已加密字段将无法解密,decrypt 会抛出 Illuminate\Contracts\Encryption\DecryptException 错误。
常见错误现象:
- 本地开发用一个
APP_KEY,上线后用另一个 → 所有加密字段读出来是空或报错 - 多台服务器没同步
APP_KEY→ 部分机器写入、部分机器读不出 - 误删
.env后重新php artisan key:generate→ 历史加密数据永久不可恢复
必须提前备份原始 APP_KEY,生产环境严禁随意轮换。
加密字段不支持查询条件(WHERE)、排序(ORDER BY)、索引加速
因为数据以 AES-256-CBC 加密后 Base64 存储,数据库层面看到的是随机字符串,无法做等值比较或范围扫描。试图写 where('ssn', '123-45-6789') 永远查不到结果。
使用场景限制:
- 只能用于「仅读写、不参与查询逻辑」的敏感字段,如身份证号、银行卡号、密钥本身
- 如果业务需要按加密字段搜索(例如「找某用户的 token」),得额外建一个非加密的哈希标识字段(如
token_hash),用hash_hmac('sha256', $token, config('app.key'))存储 - MySQL 的函数索引、PostgreSQL 的表达式索引对加密字段无效
自定义加密策略要改 App\Providers\AppServiceProvider,不是模型里加方法
默认 encrypt 使用 OpenSSL + APP_KEY,没法换算法或加盐。若需 AES-GCM、带 nonce 的加密,或对接外部 KMS,必须替换 Laravel 的 Encrypter 实现。
实操路径:
- 写一个实现
Illuminate\Contracts\Encryption\Encrypter接口的新类 - 在
AppServiceProvider::register()中用$this->app->singleton(Encrypter::class, ...)替换绑定 - 注意:所有 Eloquent
encryptcasting、encrypt()/decrypt()辅助函数都会走这个新实例
别在模型里写 setSsnAttribute 手动加密——它和 $casts 不协同,读写行为不一致,调试时容易漏掉一侧。
$casts = ['xxx' => 'encrypt'],就等于放弃数据库侧的可检索性、可索引性、可迁移性。很多人卡在“为什么查不到”,其实问题出在一开始就不该把要查的字段加密。










