| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- <?php
-
- namespace app\common\middleware;
-
- use think\facade\Cache;
- use think\facade\Log;
- use think\Request;
- use think\Response;
-
- class SignatureMiddleware
- {
- public function handle(Request $request, \Closure $next)
- {
- // 获取必要参数
- $essential = $request->only(['timestamp', 'nonce', 'signature']);
-
- // 验证必要参数
- if (count(array_filter($essential)) !== 3) {
- return $this->errorResponse('参数不完整', 400);
- }
-
- // 验证时间戳(5分钟内有效)
- if (abs(time() - (int)$essential['timestamp']) > 300) {
- return $this->errorResponse('请求已过期', 400);
- }
-
- // 验证nonce唯一性
- $nonceKey = 'nonce:' . $essential['nonce'];
- if (Cache::has($nonceKey)) {
- return $this->errorResponse('重复请求', 400);
- }
-
- // 构造签名数据(与前端完全一致)
- $signData = $request->param();
- unset($signData['signature']);
-
- // 按键名排序
- ksort($signData);
-
- // 生成签名字符串(与前端相同格式)
- $signContent = '';
- foreach ($signData as $key => $value) {
- $signContent .= "{$key}={$value}&";
- }
- $signContent = rtrim($signContent, '&');
-
- // 记录原始签名内容(用于调试)
- Log::debug("Sign Content: " . $signContent);
-
- // 获取公钥
- $publicKey = openssl_pkey_get_public(
- file_get_contents(env('RSA_PUBLIC_KEY_PATH'))
- );
-
- if (!$publicKey) {
- Log::error("公钥加载失败");
- return $this->errorResponse('系统错误', 500);
- }
-
- // 计算签名的MD5值(与前端一致)
- $md5Hash = md5($signContent);
- Log::debug("MD5 Hash: " . $md5Hash);
-
- // 解码前端签名(前端使用公钥加密)
- $signature = base64_decode($essential['signature']);
-
- // 使用公钥解密签名
- $decrypted = '';
- $success = openssl_public_decrypt($signature, $decrypted, $publicKey);
-
- if (!$success) {
- Log::error("签名解密失败: " . openssl_error_string());
- return $this->errorResponse('签名验证失败', 403);
- }
-
- Log::debug("解密结果: " . $decrypted);
-
- // 比较解密后的值与MD5哈希
- if ($decrypted !== $md5Hash) {
- Log::error("签名验证失败: 期望 {$md5Hash}, 实际 {$decrypted}");
- return $this->errorResponse('签名验证失败', 403);
- }
-
- // 记录已使用的nonce(5分钟过期)
- Cache::set($nonceKey, 1, 300);
-
- return $next($request);
- }
-
- private function errorResponse(string $message, int $code): Response
- {
- return json([
- 'code' => $code,
- 'msg' => $message,
- 'data' => null
- ])->code($code);
- }
- }
|