thinkphp的request对象不支持自动加解密,需在中间件中通过php://input或$_get/$_post原始数据统一解密并覆写超全局变量,避免框架过滤干扰;加密应使用openssl_encrypt(aes-gcm/cbc),密钥从配置注入,iv需随机且随文传输,base64编码后须url安全化。

ThinkPHP 的 Request 对象本身不支持参数加解密,必须手动拦截处理
ThinkPHP 原生的 Request 类只做参数解析和过滤,不会自动对 GET/POST 数据做加解密。如果你看到“请求参数加密”需求,实际要做的不是改框架源码,而是在控制器入口或中间件里提前处理原始输入流 —— 否则所有 input()、param()、post() 等方法拿到的都是明文,加密逻辑就失去了意义。
常见错误是直接在控制器里对 input('data') 的结果做解密,但此时参数可能已被框架自动过滤(比如开启 default_filter)、转义或类型转换,导致密文损坏无法还原。
- 务必从
file_get_contents('php://input')或$_GET/$_POST原始数组读取,避开框架封装层 - 若使用 JSON 请求体,注意
php://input只能读一次,后续再调用会为空 - 加解密密钥不能硬编码在控制器里,应通过配置项(如
config('app.crypt_key'))或环境变量注入
如何在中间件中统一解密 POST/JSON 请求参数
这是最干净的做法:把解密逻辑下沉到中间件,解密后重新写入 $_POST 和 $_GET,后续所有 Request 方法都能透明使用。但要注意 Content-Type 判断和重复解密风险。
示例中间件片段(TP6):
public function handle($request, \Closure $next)
{
$contentType = $request->header('content-type', '');
$rawData = '';
if (stripos($contentType, 'application/json') !== false) {
$rawData = file_get_contents('php://input');
} elseif ($_POST || $_GET) {
// 仅当有原始表单数据时才尝试解密,避免干扰空请求
$rawData = http_build_query($_POST) . '&' . http_build_query($_GET);
}
if ($rawData && $this->isEncrypted($rawData)) {
$decrypted = $this->decrypt($rawData);
parse_str($decrypted, $params);
// 覆盖原始超全局变量(需确保未被 register_globals 影响)
$_POST = $params;
$_GET = array_merge($_GET, $params);
}
return $next($request);
}
-
isEncrypted()可基于固定前缀(如"ENC_".base64_encode(...))或头部字段判断,避免误解普通请求 - 不要直接修改
$request实例的内部属性(如$request->param),TP6 的Request是只读缓存设计,改了也不生效 - 如果前端传的是 JSON,解密后得到的仍是 JSON 字符串,需
json_decode($decrypted, true)再parse_str()或直接赋值给$_POST
param() 和 input() 的行为差异会影响解密后取值
即使你在中间件里重写了 $_POST,param('name') 和 input('name') 返回结果仍可能不同 —— 因为前者合并了路由变量、GET、POST,并做了默认过滤;后者只读指定来源且不过滤(除非显式传第 2 个参数)。
- 解密后想保持原语义,建议统一用
input('name', '', 'htmlspecialchars'),并把过滤逻辑放在解密之后 - 若参数名含点号(如
user.name),param()会自动转成数组,但input()默认不解析,需手动input('user.name', '', null, true)(TP6.1+ 支持第 4 参数启用点号解析) - 加密参数若含特殊字符(如 +、/、=),Base64 编码后未 URL 安全化会导致
$_GET解析失败,应在加密后做strtr($encoded, '+/', '-_')
加密算法选型与性能陷阱
别用 md5() 或 sha1() —— 它们是哈希,不可逆。生产环境推荐 openssl_encrypt()(AES-128-CBC 或 AES-256-GCM),但要注意 IV(初始化向量)必须随机且随密文一起传输,否则无法解密。
- GCM 模式支持认证加密,能防篡改,但 PHP 7.1+ 才完整支持;CBC 模式需手动校验 padding 和 MAC
- 避免在每次请求中都调用
openssl_random_pseudo_bytes()生成 IV,可复用或从请求头带入(前提是前端可控) - TP 自带的
think\helper\Str::random()不适合生成加密 IV,它不保证密码学安全 - 加解密耗时明显(尤其 GCM),高并发下建议用 Swoole 协程或提前预热 OpenSSL 上下文
真正难的不是写几行加解密代码,而是密钥轮换、IV 管理、错误密文的降级处理(比如解密失败时返回 400 还是静默忽略),这些细节一旦漏掉,线上就只能靠日志硬查。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











