解密必须在中间件中完成并注入$request,否则$_post和$request->post()均为空或乱码;需检查x-encrypted头、读php://input、base64_decode后aes解密,并用$request->merge(['post' => $decrypted])注入解密数据。

ThinkPHP 里做请求数据解密,核心不是“写个函数调用一下”,而是必须在框架解析请求体之前完成——否则 $_POST、$request->post() 全是空的或乱码。中间件是唯一可靠入口,且必须处理好流重置和数据注入。
为什么解密逻辑不能放在控制器里
因为 ThinkPHP 的 $request->post() 和 input('post.') 都依赖 PHP 原生的 php://input 流或 $_POST 超全局变量。而加密请求体(比如 AES 密文)是 base64 编码后的二进制串,直接丢给框架解析会触发自动 URL 解码、+ 变空格、= 被截断等行为,最终导致 openssl_decrypt() 报错 data length is not multiple of block size 或返回 false。
- 微信小程序传
encryptedData时若用input('encryptedData'),+ 号被转成空格,base64_decode 失败 -
$request->post()默认会对值做 trim() 和过滤,破坏密文完整性 - 控制器执行时,原始请求体已不可逆地被消费过一次
解密中间件必须做的三件事
一个可用的解密中间件不是“调用 openssl_decrypt 就完事”,它要接管输入流、还原数据结构、并让后续所有读取方式都生效。
- 检查请求头是否含
X-Encrypted: aes-128-cbc或限定路径(如/api/v1/secure),避免全局解密误伤普通接口 - 用
file_get_contents('php://input')读原始密文,再base64_decode()→openssl_decrypt();注意 IV 和密钥必须与前端一致,IV 每次请求随机生成并随密文传输 - 解密成功后,不能只改
$_POST,要用$request->merge(['post' => $decryptedData])注入,这样$this->request->post()、input('post.name')、验证器、模型赋值全都能正常取到
密钥和 IV 怎么传才安全
密钥绝不能硬编码在 JS 里,也不能出现在响应体中;IV 可以随请求传输,但必须每次随机,不能复用。
- 密钥从环境变量读:
env('AES_KEY'),并在.env中配置,确保不进 Git - IV 必须是 16 字节(AES-128-CBC),由前端生成并附在请求头(如
X-IV: xxx)或请求体字段中,服务端base64_decode()后直接传给openssl_decrypt() - 别用
time()或字符串拼接当密钥——openssl_encrypt()要求密钥长度严格匹配:AES-128 要 16 字节,AES-256 要 32 字节,少一字节都会返回 false
常见报错和对应修复点
这些错误基本都指向同一个问题:密文没对齐、参数类型错、或流程插太晚。
-
Warning: openssl_decrypt(): IV passed is only 15 bytes long, cipher expects an IV of precisely 16 bytes, padding with \0→ IV 长度不对,检查 base64_decode 后是否真为 16 字节,注意换行符和空格 -
bool(false)返回且无报错 → 密钥长度错、算法名写成AES-128-CBC但实际用了 256 位密钥,或密文被多次 URL 解码损坏 - 解密后
json_decode()出来是 null → 解密结果含不可见控制字符,用trim($decrypted)清理前后空白,再检查是否为合法 JSON
真正难的不是写 decrypt 函数,而是把解密动作卡在框架解析请求体的前一刻,并让整个请求生命周期都感知到这个“已解密”的状态。漏掉 merge() 或错判 IV 来源,接口就永远拿不到真实数据。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











