Files
crm_project/backend/app/middleware/audit.py
T
hankin 423baff73b v0.1.0: CRM/ERP 系统内测版本 - 安全加固完成
- Docker bridge 网络隔离(8000 端口封死)
- Gunicorn 4 Worker 多进程
- Alembic 数据库迁移基线
- 日志轮转 20m×3
- JWT 密钥 + DB 密码 + CORS 收紧
- 3-2-1 备份链路(NAS + R740-B 冷备)
- 连接池 pool_pre_ping + pool_recycle=3600
2026-03-16 07:31:37 +00:00

60 lines
2.0 KiB
Python

# -*- coding: utf-8 -*-
"""
全局审计中间件
拦截所有入站 HTTP 请求,记录:方法、URL、客户端 IP、耗时、响应状态码。
日志输出到标准 logging,生产环境可对接 ELK / Loki 等日志收集系统。
"""
import logging
import time
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
from starlette.responses import Response
# 配置审计专用 logger,与业务日志分离
audit_logger = logging.getLogger("audit")
audit_logger.setLevel(logging.INFO)
class AuditMiddleware(BaseHTTPMiddleware):
"""
审计中间件 - 记录每个请求的关键信息。
日志格式: [AUDIT] <客户端IP> <方法> <URL> <状态码> <耗时ms>
"""
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
# 提取客户端真实 IP (优先取反向代理传递的 X-Forwarded-For)
client_ip = request.headers.get(
"X-Forwarded-For", request.client.host if request.client else "unknown"
)
method = request.method
url = str(request.url)
start_time = time.perf_counter()
try:
response = await call_next(request)
except Exception:
# 未捕获异常也要记录审计日志
elapsed_ms = (time.perf_counter() - start_time) * 1000
audit_logger.error(
"[AUDIT] %s %s %s 500 %.1fms (unhandled exception)",
client_ip, method, url, elapsed_ms,
)
raise
elapsed_ms = (time.perf_counter() - start_time) * 1000
audit_logger.info(
"[AUDIT] %s %s %s %d %.1fms",
client_ip, method, url, response.status_code, elapsed_ms,
)
# 将审计信息注入响应头 (方便调试,生产环境可移除)
response.headers["X-Request-Duration-Ms"] = f"{elapsed_ms:.1f}"
return response