getRealMethod(); if (!config('api_crypto.enabled') || $method === 'OPTIONS' || !$request->is( 'api/wallet/*', 'api/newPc', 'api/newPc/*', 'api/issue', 'api/issue/*', 'api/pcissue', 'api/pcissue/*', )) { return $next($request); } try { $crypto = new ApiCryptoService( (string) config('api_crypto.key'), (int) config('api_crypto.timestamp_tolerance', 300), (int) config('api_crypto.max_payload_bytes', 2097152), ); } catch (RuntimeException) { return $this->error(5001, 'API 加密配置错误,请联系服务端管理员', 500)->prepare($request); } if ($request->headers->get(self::HEADER) !== '1') { if (config('api_crypto.allow_plaintext')) { return $next($request); } return $this->encryptResponse($this->error(4001, '请求必须使用加密协议', 400), $crypto, $request); } $queryMethod = in_array($method, ['GET', 'HEAD'], true); try { $payload = $queryMethod ? ($request->query->all()['payload'] ?? null) : ($request->isJson() ? $request->json('payload') : null); if (!is_string($payload) || $payload === '') { throw new ApiCryptoException('请求缺少加密 payload'); } $parameters = $crypto->decrypt($payload, ApiCryptoService::DIRECTION_REQUEST); $this->replaceParameters($request, $parameters, $queryMethod); } catch (ApiCryptoException | JsonException $exception) { return $this->encryptResponse($this->error(4002, $exception->getMessage(), 400), $crypto, $request); } return $this->encryptResponse($next($request), $crypto, $request); } private function replaceParameters(Request $request, array $parameters, bool $queryMethod): void { $headers = $request->headers->all(); $server = $request->server->all(); $server['REQUEST_URI'] = explode('?', $request->getRequestUri(), 2)[0]; $server['QUERY_STRING'] = ''; // The HTTP verb belongs to the transport, not to the wrapped parameters. unset($parameters['_method'], $headers['x-http-method-override'], $server['HTTP_X_HTTP_METHOD_OVERRIDE']); $headers['accept'] = ['application/json']; $server['HTTP_ACCEPT'] = 'application/json'; $body = $queryMethod ? '' : json_encode((object) $parameters, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR); unset($headers['content-length'], $server['CONTENT_LENGTH']); $request->initialize( $queryMethod ? $parameters : [], $queryMethod ? [] : $parameters, $request->attributes->all(), $request->cookies->all(), [], $server, $body, ); $request->headers->replace($headers); $request->setJson($queryMethod ? $request->query : $request->request); } private function encryptResponse(Response $response, ApiCryptoService $crypto, Request $request): Response { $response->headers->set('Cache-Control', 'no-store'); $response->headers->set(self::HEADER, '1'); $response->headers->remove('Content-Length'); $response->headers->remove('ETag'); if ($request->getRealMethod() === 'HEAD' || $response->isEmpty()) { $response->setContent(''); return $response; } try { $data = json_decode((string) $response->getContent(), false, 512, JSON_THROW_ON_ERROR); $envelope = ['encrypted' => true, 'payload' => $crypto->encrypt($data, ApiCryptoService::DIRECTION_RESPONSE)]; if ($response instanceof JsonResponse) { $response->setData($envelope); } else { $response->setContent(json_encode($envelope, JSON_THROW_ON_ERROR)); $response->headers->set('Content-Type', 'application/json'); } return $response; } catch (ApiCryptoException | JsonException) { return $this->error(5002, 'API 响应加密失败', 500); } } private function error(int $code, string $message, int $status): JsonResponse { return new JsonResponse(['code' => $code, 'show' => 1, 'msg' => $message, 'data' => []], $status, [ 'Cache-Control' => 'no-store', ]); } }