应在中间件中用 $request->getcontent() 获取加密内容,aes解密后验证json合法性,再通过反射清空 $request->json 缓存并确保 content-type 为 application/json,同时必须校验 hmac 签名与时间戳。

中间件里怎么解密请求体(Illuminate\Http\Request 的 $request->getContent() 已被加密)
Laravel 中间件执行时,原始请求体还没被 Laravel 解析成数组或 JSON,所以不能直接用 $request->all() 或 $request->json()——它们依赖已解析的输入,而加密后的内容是乱码,会解析失败。
正确做法是在中间件中手动读取原始加密内容,解密后再替换请求体,让后续逻辑照常运行。关键点:必须用 $request->instance() 重建请求对象,并确保解密后的内容格式(如 JSON)合法。
- 先调用
$request->getContent()获取原始加密字符串(不是$request->input()) - 用对称加密(如 AES-256-CBC)解密,注意 IV 和密钥必须与前端一致
- 解密后得到的是 JSON 字符串?用
json_decode($decrypted, true)验证结构,避免后续json()方法抛InvalidArgumentException - 用
Request::create()或反射重置$request->json()内部缓存(更稳妥的做法是用$request->replace($data)替换 input 数组,但仅对 form-data 有效;对 raw JSON,需配合$request->merge()+ 重写getContent()闭包——实际推荐用自定义Request类替代)
Laravel 10+ 中如何让 $request->json() 返回解密后的数据
默认 $request->json() 缓存了第一次解析结果,中间件里改了原始内容但没清缓存,它还是返回旧的(或 null)。这不是 bug,是设计使然。
绕过缓存的最简方式:不依赖 json(),而是自己解密 + json_decode();但如果坚持走标准流程,必须重置请求的「已解析」状态。
- 在中间件中调用
$request->json()->all()前,先执行$request->offsetUnset('json');(Laravel 9+ 支持) - 或者更通用:用反射清除
Illuminate\Http\Request的$json属性($ref = new \ReflectionProperty($request, 'json'); $ref->setAccessible(true); $ref->setValue($request, null);) - 确保解密后的内容是合法 JSON 字符串,否则
json()仍会返回空实例——可加if (json_last_error() !== JSON_ERROR_NONE) { abort(400, 'Invalid encrypted payload'); }
加密中间件里要不要校验签名(HMAC)
只加密不签名,攻击者可以重放、篡改密文再提交,服务端解密后照单全收。加密 ≠ 安全,只是保密;签名才是防篡改的关键。
典型错误:前端只 AES 加密,后端只解密,忽略时间戳和签名验证。结果是“看似加密,实则裸奔”。
- 前端应在加密前生成 HMAC-SHA256(用密钥 + 时间戳 + 原始 JSON 字符串),附在请求头如
X-Signature - 中间件里先检查
request->header('X-Timestamp')是否超时(如 ±300 秒),再拼接相同字段重新算签名比对 - 签名密钥不要和加密密钥相同,避免密钥泄露导致双重失效
- 别把签名逻辑写在 Controller 里——必须在中间件早期拦截,否则非法请求已进入业务层
为什么 app/Http/Middleware/EncryptRequest.php 里用 $request->getRealPath() 会报错
$request->getRealPath() 只对上传文件有效,对普通请求体返回 false 或空字符串。试图用它读加密 body 是典型误用,会导致解密失败且无提示。
真正该用的是 $request->getContent(),但它有陷阱:如果请求是 multipart/form-data,getContent() 返回空(因为 Laravel 已把 body 读进 $_FILES),此时得从 php://input 重读——但 PHP 默认不允许重复读。
- 优先判断
$request->isJson()或$request->header('Content-Type') === 'application/json' - 确认是 JSON 请求后才用
$request->getContent();否则跳过解密(或按 form-data 规则处理字段级加密) - 如果必须支持混合类型,中间件开头加
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST') { rewind(STDIN); }不可靠,应统一约定 Content-Type
$request->json() 还是空,其实问题不在解密本身,而在没清 Laravel 对 JSON 的缓存引用。










