使用 Django 和 DRF 进行审计日志

Ama*_*mar 4 python django audit-logging django-rest-framework django-auditlog

我需要在我使用Django 1.8和的项目之一中实现审核日志功能Django-Rest-Framework 3.2.2。我已经扩展BaseUserManager class了创建用户模型,因为我必须在我的应用程序中使用电子邮件作为用户名(如果此信息很重要)。

下面是我的数据库设计,它将保存日志:

**fields    type    desc**

id           pk      ( auto_increment)  
cust_id   FK  customer 
customer_name   FK  customer
user_id FK  user
user_name   FK  user
module  Varchar(100) sales,order,billing,etc
action  Varchar(10) Create/Update/Delete
previous_value  varchar(500)    
current_value   varchar(500)    
Datetime    Datetime    timestamp of change
Run Code Online (Sandbox Code Playgroud)

我已经尝试过https://pypi.python.org/pypi/django-audit-log但根据我的要求它有两个问题 -

  1. 它没有按照我的要求捕获数据,我理解这是我的问题,因此我修改了它的代码并将我的字段添加到它的模型中。
  2. 它不捕获模块信息。行为是随机的。

我正在寻求建议以继续使用此功能。哪个包最适合我的任务。

PS我也尝试过Django-reversion,我没有数据版本控制的要求。

谢谢

Ama*_*mar 5

我通过修改auditlog代码实现了我所需要的 -

  1. 在auditlog的LogEntry模型中添加了必填字段。
  2. 修改receivers.py的log_create、log_update、log_delete函数,将信息保存在新添加的字段中。

使用这个我就完成了一半。现在我面临的唯一问题是,由于 1 个表的模型实例也包含其他表的信息,因为该表中使用了 FK。

为了解决这个问题,我可以想出一个效果很好的解决方案,但我对此不满意。我在每个模型中添加了一个像include_in_model()这样的函数,并修改了auditlog的registry.py register()函数来获取这些字段,并且仅使用它来保存LogEntry模型中的信息。

这种方法将要求我在每个模型类中创建此include_in_model()函数,并传递特定模型所需的字段。这样我就避免了与 FK 相关的信息。


Dan*_*men 5

Django Simple History是一个优秀的应用程序,我过去在生产项目中使用过,它将为您提供针对用户的每个模型的审核。

此外,您应该创建自己的身份验证类,该类将负责记录请求。假设用户使用令牌对您的 API 进行身份验证。它会在每个 HTTP 请求的标头中发送到您的 API,如下所示Authorization: Bearer <My Token>:然后我们应该记录与请​​求相关的用户、时间、用户的 IP 和正文。

这很简单:

设置.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'common.authentication.MyTokenAuthenticationClass'
    ),
    ...
}
Run Code Online (Sandbox Code Playgroud)

通用/身份验证.py

from django.utils import timezone
from django.utils.translation import ugettext_lazy as _

from ipware.ip import get_real_ip
from rest_framework import authentication
from rest_framework import exceptions

from accounts.models import Token, AuditLog


class MyTokenAuthenticationClass(authentication.BaseAuthentication):

    def authenticate(self, request):

        # Grab the Athorization Header from the HTTP Request
        auth = authentication.get_authorization_header(request).split()

        if not auth or auth[0].lower() != b'bearer':
            return None

        # Check that Token header is properly formatted and present, raise errors if not
        if len(auth) == 1:
            msg = _('Invalid token header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid token header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            token = Token.objects.get(token=auth[1])
            # Using the `ipware.ip` module to get the real IP (if hosted on ElasticBeanstalk or Heroku)
            token.last_ip = get_real_ip(request)
            token.last_login = timezone.now()
            token.save()

            # Add the saved token instance to the request context
            request.token = token

        except Token.DoesNotExist:
            raise exceptions.AuthenticationFailed('Invalid token.')

        # At this point, insert the Log into your AuditLog table:
        AuditLog.objects.create(
            user_id=token.user,
            request_payload=request.body,
            # Additional fields
            ...
        )

        # Return the Authenticated User associated with the Token
        return (token.user, token)
Run Code Online (Sandbox Code Playgroud)