ApiRequestLogMiddleware.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. <?php
  2. declare (strict_types=1);
  3. namespace app\middleware;
  4. use app\common\model\system\ApiRequestLog;
  5. use crmeb\services\JwtTokenService;
  6. use think\facade\Request;
  7. use think\Response;
  8. class ApiRequestLogMiddleware
  9. {
  10. /**
  11. * 处理请求
  12. *
  13. * @param \think\Request $request
  14. * @param \Closure $next
  15. * @return Response
  16. */
  17. public function handle($request, \Closure $next)
  18. {
  19. // 记录请求开始时间
  20. $startTime = microtime(true);
  21. // 执行请求
  22. $response = $next($request);
  23. // 记录请求结束时间和计算响应时间
  24. $endTime = microtime(true);
  25. $responseTime = (int)(($endTime - $startTime) * 1000); // 转换为毫秒
  26. try {
  27. // 获取应用类型
  28. $appType = $this->getAppType($request);
  29. // 从token中获取所有用户信息
  30. $userInfo = $this->getUserInfoFromToken($request);
  31. $userId = $userInfo['userId'] ?? 0;
  32. $merchantId = $userInfo['merchantId'] ?? 0;
  33. $adminId = $userInfo['adminId'] ?? 0;
  34. // 记录请求日志
  35. ApiRequestLog::record([
  36. 'app_type' => $appType,
  37. 'request_method' => $request->method(),
  38. 'request_url' => $request->url(),
  39. 'request_params' => $this->getRequestParams($request),
  40. 'request_headers' => $this->getRequestHeaders($request),
  41. 'request_ip' => $request->ip(),
  42. 'user_agent' => $request->header('user-agent', ''),
  43. 'user_id' => $userId,
  44. 'merchant_id' => $merchantId,
  45. 'admin_id' => $adminId,
  46. 'response_code' => $response->getCode(),
  47. 'response_data' => $this->getResponseData($response),
  48. 'response_time' => $responseTime,
  49. 'error_message' => $this->getErrorMessage($response),
  50. ]);
  51. } catch (\Exception $e) {
  52. // 记录日志失败时不中断主流程
  53. // 可以在这里添加日志记录失败的日志
  54. }
  55. return $response;
  56. }
  57. /**
  58. * 获取应用类型
  59. * @param \think\Request $request
  60. * @return string
  61. */
  62. private function getAppType($request): string
  63. {
  64. // 获取请求路径
  65. $path = $request->pathinfo();
  66. $position = strpos($path, '/');
  67. if ($position === false) {
  68. return 'unknown';
  69. }
  70. return substr($path, 0, $position);
  71. }
  72. /**
  73. * 获取请求参数
  74. * @param \think\Request $request
  75. * @return array
  76. */
  77. private function getRequestParams($request): array
  78. {
  79. $params = $request->param();
  80. // 过滤敏感信息
  81. $sensitiveFields = ['password', 'pwd', 'token', 'secret', 'key', 'authorization'];
  82. foreach ($sensitiveFields as $field) {
  83. if (isset($params[$field])) {
  84. $params[$field] = '***';
  85. }
  86. }
  87. return $params;
  88. }
  89. /**
  90. * 获取请求头信息
  91. * @param \think\Request $request
  92. * @return array
  93. */
  94. private function getRequestHeaders($request): array
  95. {
  96. $headers = $request->header();
  97. // 过滤敏感头信息
  98. $sensitiveHeaders = ['authorization', 'cookie', 'token'];
  99. foreach ($sensitiveHeaders as $header) {
  100. if (isset($headers[$header])) {
  101. $headers[$header] = '***';
  102. }
  103. }
  104. return $headers;
  105. }
  106. /**
  107. * 获取响应数据
  108. * @param Response $response
  109. * @return array|null
  110. */
  111. private function getResponseData(Response $response): ?array
  112. {
  113. $content = $response->getContent();
  114. try {
  115. $data = json_decode($content, true);
  116. if (json_last_error() === JSON_ERROR_NONE) {
  117. return $data;
  118. }
  119. } catch (\Exception $e) {
  120. // JSON解析失败,返回原始内容的前1000个字符
  121. return ['raw_content' => substr($content, 0, 1000)];
  122. }
  123. return null;
  124. }
  125. /**
  126. * 获取错误信息
  127. * @param Response $response
  128. * @return string
  129. */
  130. private function getErrorMessage(Response $response): string
  131. {
  132. if ($response->getCode() >= 400) {
  133. $content = $response->getContent();
  134. try {
  135. $data = json_decode($content, true);
  136. if (json_last_error() === JSON_ERROR_NONE && isset($data['msg'])) {
  137. return $data['msg'];
  138. }
  139. } catch (\Exception $e) {
  140. return substr($content, 0, 500);
  141. }
  142. }
  143. return '';
  144. }
  145. /**
  146. * 从token中解析adminId
  147. * @param \think\Request $request
  148. * @return int
  149. */
  150. private function getAdminIdFromToken($request): int
  151. {
  152. try {
  153. $token = $this->getTokenFromRequest($request);
  154. if (!$token) {
  155. return 0;
  156. }
  157. $jwtService = new JwtTokenService();
  158. try {
  159. // 尝试解析token
  160. $payload = $jwtService->parseToken($token);
  161. } catch (\Exception $e) {
  162. // 如果解析失败,尝试解码(不验证签名)
  163. $payload = $jwtService->decode($token);
  164. }
  165. // 检查token类型是否为admin,并且包含有效的ID
  166. if (isset($payload->jti) && is_array($payload->jti) && count($payload->jti) >= 2) {
  167. if ($payload->jti[1] === 'admin') {
  168. return (int)$payload->jti[0];
  169. }
  170. }
  171. } catch (\Exception $e) {
  172. // token解析失败,返回0
  173. return 0;
  174. }
  175. return 0;
  176. }
  177. /**
  178. * 从请求中获取token
  179. * @param \think\Request $request
  180. * @return string|null
  181. */
  182. private function getTokenFromRequest($request): ?string
  183. {
  184. // 从header中获取token
  185. $token = $request->header('X-Token');
  186. if ($token && strpos($token, 'Bearer ') === 0) {
  187. $token = substr($token, 7);
  188. }
  189. // 如果header中没有,尝试从参数中获取
  190. if (!$token) {
  191. $token = $request->param('token');
  192. }
  193. return $token ? trim($token) : null;
  194. }
  195. /**
  196. * 从token中获取所有用户信息
  197. * @param \think\Request $request
  198. * @return array
  199. */
  200. private function getUserInfoFromToken($request): array
  201. {
  202. $result = [
  203. 'userId' => 0,
  204. 'merchantId' => 0,
  205. 'adminId' => 0
  206. ];
  207. try {
  208. $token = $this->getTokenFromRequest($request);
  209. if (!$token) {
  210. return $result;
  211. }
  212. $jwtService = new JwtTokenService();
  213. try {
  214. // 尝试解析token
  215. $payload = $jwtService->parseToken($token);
  216. } catch (\Exception $e) {
  217. // 如果解析失败,尝试解码(不验证签名)
  218. $payload = $jwtService->decode($token);
  219. }
  220. // 检查token类型并获取对应的ID
  221. if (isset($payload->jti) && is_array($payload->jti) && count($payload->jti) >= 2) {
  222. $userId = (int)$payload->jti[0];
  223. $userType = $payload->jti[1];
  224. switch ($userType) {
  225. case 'user':
  226. $result['userId'] = $userId;
  227. break;
  228. case 'mer':
  229. $result['merchantId'] = $userId;
  230. break;
  231. case 'admin':
  232. $result['adminId'] = $userId;
  233. break;
  234. }
  235. }
  236. } catch (\Exception $e) {
  237. // token解析失败,返回默认值
  238. }
  239. return $result;
  240. }
  241. }