django中间件中需在sessionmiddleware和authenticationmiddleware之后获取request.user,通过if hasattr(request, 'user') and request.user.is_authenticated双重校验避免anonymoususer错误;操作类型用request.method + request.path拼接,敏感数据需白名单过滤并脱敏。

中间件里怎么拿到用户和请求操作信息
Django中间件的 process_request 或 __call__ 方法能访问到 request 对象,但默认不保证已认证——必须确认用户已登录且 request.user 是真实 User 实例,而不是 AnonymousUser。常见错误是直接取 request.user.username 导致 AttributeError 或记录为 AnonymousUser。
实操建议:
- 用
if hasattr(request, 'user') and request.user.is_authenticated:做双重检查,比单纯判request.user.is_anonymous更稳妥 - 操作类型(如 "GET /api/order/123")建议拼接
request.method和request.path,不要用request.get_full_path()(含 querystring 容易污染日志或泄露敏感参数) - 若需记录请求体(如 POST 数据),注意
request.body只能读一次,且可能为空(例如表单提交走request.POST);建议只在必要接口(如管理后台)中按需解析,避免性能损耗
审计日志该存到哪里:数据库 vs 日志文件
存数据库(如新建 AuditLog 模型)便于查询、关联用户、做后台展示;存文件(logging 模块写入 audit.log)更轻量、不拖慢请求,但查起来费劲、难结构化。
实操建议:
- 高并发业务(如 API 网关层)优先用文件 + 日志采集(如 Filebeat → ES),避免 ORM 写入阻塞响应
- 管理后台类场景可用数据库,但务必给
AuditLog的user、timestamp字段加索引,否则按用户查日志会变慢 - 别在中间件里直接调
MyModel.objects.create()—— 万一模型字段校验失败或 DB 连接异常,会崩掉整个请求;改用异步任务(如 Celerylog_audit.delay(...))或至少包try/except捕获DatabaseError
如何避免中间件记录重复日志或漏记静态资源
Django 开发模式下,DEBUG=True 时 StaticFilesStorage 会由开发服务器处理,触发中间件;生产环境 Nginx 直接返回 /static/ 文件,则不进 Django 中间件——这导致日志统计口径不一致。
实操建议:
- 在中间件开头过滤路径:
if request.path.startswith('/static/') or request.path.startswith('/media/'):直接return None - CSRF 失败、404、500 等异常响应也应记录,但不能依赖
process_response(它不接收异常对象);改用process_exception钩子,配合request.resolver_match获取视图名 - 同一请求可能被多个中间件处理(如 SessionMiddleware 在前),确保你的审计中间件位置合理:放在
SessionMiddleware和AuthenticationMiddleware之后,否则request.user拿不到
敏感字段脱敏和性能开销怎么平衡
记录日志时如果原样保存密码、token、身份证号等,违反安全规范;但逐字段正则匹配又拖慢每个请求。
实操建议:
- 对
request.POST或request.GET做白名单过滤:只保留['username', 'email', 'action']等明确需要审计的键,其余一律丢弃 - 用
json.dumps(dict(request.POST), ensure_ascii=False)替代str(request.POST),避免QueryDict的字符串表示包含内存地址等无意义内容 - 单次请求日志写入耗时超过 5ms 就要警惕——可在中间件里用
time.time()打点,上线前压测验证;超时则降级为仅记录 method/path/user_id/timestamp 四个字段
真正麻烦的是跨服务场景:比如用户通过 API Gateway 调用 Django 服务,真实 IP 和操作人身份藏在 header 里,这时中间件得额外解析 request.META.get('HTTP_X_REAL_IP') 和自定义认证头,而且必须和网关约定好透传规则——这点很容易被忽略。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











