laravel加密失败主因是app_key配置错误:必须为32位无空格随机字符串,可用php artisan key:generate生成并检查.env是否完整;密钥变更会导致旧密文无法解密;encrypt()与encryptstring()不可混用;解密异常需按decryptexception、openssl异常、mac无效三类分别处理;敏感字段加密应结合模型访问器/修改器实现自动加解密,并注意字段类型设为text。

加密前必须确认 APP_KEY 是否有效
没配好 APP_KEY 是 Laravel 加密失败最常见原因,不是“忘了配”,而是配得不对:它必须是 32 字符随机字符串,且不能含空格或换行。用 php artisan key:generate 生成后,检查 .env 文件里 APP_KEY 值是否完整、没被截断或手动改短。
- 如果手动编辑过
.env,复制粘贴时容易带隐藏字符,建议用cat -A .env查看 -
APP_KEY变更后,之前用 Crypt 加密的数据全部无法解密,生产环境切勿随意重置 - 运行
php artisan tinker后执行Crypt::encryptString('test')报RuntimeException: The only supported ciphers are AES-128-CBC and AES-256-CBC,大概率是APP_KEY长度不对(比如只有 24 位)
Crypt::encrypt() 和 Crypt::encryptString() 别混用
两者底层都走 OpenSSL,但输入类型和序列化行为不同:Crypt::encrypt() 接收任意 PHP 值(自动序列化),Crypt::encryptString() 只接受字符串,不序列化。混用会导致解密时报 DecryptException: The payload is invalid。
- 加密数组或对象,必须用
Crypt::encrypt($data);解密时用Crypt::decrypt($encrypted) - 只加密纯字符串(如 token、手机号),优先用
Crypt::encryptString($str);解密对应用Crypt::decryptString($encrypted) - 数据库存加密字段时,若字段类型是
TEXT,用encryptString更安全——避免序列化产生的额外字符引发截断
解密失败时先看异常堆栈里的具体错误
Laravel Crypt 抛出的异常看似统一,但实际分三类,处理方式完全不同:
-
Illuminate\Contracts\Encryption\DecryptException: The payload is invalid→ 多数是密文被修改、截断,或用了错误的APP_KEY -
OpenSSL Encrypt Exception→ PHP OpenSSL 扩展未启用,或openssl_cipher_iv_length('AES-256-CBC')返回 false(PHP 版本太低或编译参数问题) -
DecryptException: The MAC is invalid→ 密文完整性校验失败,说明密文在传输/存储中被篡改,不是配置问题
别急着重写逻辑,先用 dd($encrypted) 打印密文,对比前后是否一致;再确认解密代码和加密代码跑在同一个 APP_KEY 环境下(比如 CLI 和 Web 请求可能加载不同 env)。
敏感字段加密别只靠 Crypt,得配合模型访问器
单纯在控制器里 Crypt::encrypt($request->ssn) 再存库,后续读取还得手动解密,极易遗漏或出错。正确做法是把加解密逻辑下沉到 Eloquent 模型里,用访问器(accessor)和修改器(mutator)封装。
- 在模型中定义
setSsnAttribute($value),内部调用Crypt::encryptString($value) - 定义
getSsnAttribute($value),内部调用Crypt::decryptString($value) - 注意:数据库字段类型要设为
TEXT,因为加密后字符串长度远超原始值(比如 11 位手机号加密后约 140 字符) - 如果该字段需用于查询(如搜索加密后的手机号),Crypt 不支持模糊匹配,得换方案,比如用哈希 + 盐做确定性加密,或改用
DB::raw()配合数据库端加密函数
真正麻烦的从来不是“怎么调用 Crypt”,而是密文生命周期管理——谁有权限解密、日志会不会打密文、备份数据是否仍受保护。这些没法靠一个函数解决。










