from fastapi import Request from starlette.middleware.base import BaseHTTPMiddleware import time from app.core.logger import log_request_async from app.core.security import verify_token, decode_jwt import json import copy class RequestLoggerMiddleware(BaseHTTPMiddleware): LOGGED_METHODS = {"POST", "PUT", "DELETE"} SENSITIVE_PATHS = { "/api/user/login", "/api/user/password-login", "/api/user/reset-password" } SENSITIVE_FIELDS = {"password", "verify_code", "old_password", "new_password"} def filter_sensitive_data(self, data: dict, path: str) -> dict: """过滤敏感数据""" if not data or path not in self.SENSITIVE_PATHS: return data filtered_data = copy.deepcopy(data) for field in self.SENSITIVE_FIELDS: if field in filtered_data: filtered_data[field] = "***" return filtered_data async def dispatch(self, request: Request, call_next): method = request.method if method not in self.LOGGED_METHODS: return await call_next(request) start_time = time.time() path = request.url.path headers = dict(request.headers) query_params = dict(request.query_params) # 获取并过滤请求体 body = None try: body_bytes = await request.body() if body_bytes: body = json.loads(body_bytes) body = self.filter_sensitive_data(body, path) except: pass # 从 Authorization 头获取 token token = None auth_header = headers.get('authorization') if auth_header and auth_header.startswith('Bearer '): token = auth_header.split(' ')[1] # 从 token 获取用户信息 user_id = None if token: try: payload = decode_jwt(token) if payload: user_id = payload.get("userid") except: pass # 处理请求 response = await call_next(request) # 计算响应时间 response_time = int((time.time() - start_time) * 1000) # 异步记录日志 log_data = { "path": path, "method": method, "headers": headers, "query_params": query_params, "body": body, "user_id": user_id, "ip_address": request.client.host, "status_code": response.status_code, "response_time": response_time } log_request_async(log_data) return response