You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Encryption trait迁移至Laravel模型$cast时旧数据解密异常

问题根因

返回值中携带的s:23:"Harry Potter";是PHP serialize() 函数的标准序列化输出,问题本质是旧第三方加密trait与Laravel 9原生encrypted cast的加解密逻辑不匹配:

  • 旧trait加密流程:原始值 → serialize()序列化 → OpenSSL加密 → 入库;解密时反向执行:库中取值 → OpenSSL解密 → unserialize()反序列化 → 返回原始值
  • Laravel原生encrypted cast流程:原始值 → OpenSSL加密 → 入库;解密时直接返回解密后的明文,不做序列化/反序列化处理
    读取旧存量数据时,解密得到的是旧逻辑写入的序列化字符串,没有经过反序列化步骤就直接返回,因此会出现多余的序列化标记。新增数据没有经过序列化步骤,所以加解密表现正常。
解决方案

不需要全量遍历修正存量数据,实现一个兼容新旧格式的自定义Cast即可,上线后无侵入兼容所有历史数据,后续数据更新时会自动迁移为原生格式。

  1. 生成自定义Cast类:
    执行artisan命令创建类文件:
php artisan make:cast EncryptedCompat
  1. 编辑生成的app/Casts/EncryptedCompat.php文件,写入如下逻辑:
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;

class EncryptedCompat implements CastsAttributes
{
    public function get(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        if (is_null($value)) {
            return null;
        }

        $decrypted = decrypt($value);

        // 识别旧存量数据的序列化格式,自动反序列化
        if ($this->isPhpSerialized($decrypted)) {
            return unserialize($decrypted);
        }

        // 新格式数据直接返回明文
        return $decrypted;
    }

    public function set(Model $model, string $key, mixed $value, array $attributes): mixed
    {
        if (is_null($value)) {
            return null;
        }

        // 新写入数据统一使用原生逻辑加密,不再做序列化
        return encrypt($value);
    }

    protected function isPhpSerialized(mixed $data): bool
    {
        if (!is_string($data)) {
            return false;
        }
        $data = trim($data);
        if ($data === 'N;') {
            return true;
        }
        if (strlen($data) < 4) {
            return false;
        }
        if ($data[1] !== ':') {
            return false;
        }
        $validTypes = ['s', 'a', 'i', 'd', 'b', 'o', 'O', 'C'];
        if (!in_array($data[0], $validTypes)) {
            return false;
        }
        return (bool)preg_match("/^{$data[0]}:[0-9]+:/s", $data);
    }
}
  1. 将模型中原本配置的encrypted相关cast替换为自定义兼容类即可:
// 原配置
protected $casts = [
    'name' => 'encrypted',
    'extra_config' => 'encrypted:array',
];

// 替换后
protected $casts = [
    'name' => \App\Casts\EncryptedCompat::class,
    // 数组/JSON类型的加密字段可根据实际存储逻辑,在get/set方法中增加json_encode/json_decode处理即可
];
方案优势
  • 无停机成本,不需要提前执行全量数据迁移脚本,上线即兼容所有新旧数据
  • 后续新增、更新数据时会自动按Laravel原生加密格式存储,存量数据会在日常业务更新对应记录时自动完成格式转换,无额外维护成本
  • 待所有存量数据完成格式转换后,可直接将cast改回Laravel原生encrypted类型,没有历史包袱

内容的提问来源于stack exchange,提问作者Inigo EC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 13:24:22