使用Django Rest Framework进行非用户连接的自定义身份验证

时间:2015-06-24 00:10:07

标签: django authentication django-rest-framework django-authentication

我已使用TokenAuthentication

启用了使用DRF的用户身份验证
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
         'rest_framework.authentication.TokenAuthentication',
         'rest_framework.authentication.SessionAuthentication'
    ),
    'DEFAULT_MODEL_SERIALIZER_CLASS':
        'rest_framework.serializers.ModelSerializer',
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.AllowAny',
    ),
    #'EXCEPTION_HANDLER': 'apps.core.exceptions.custom_exception_handler'

}

我有以下型号:

class Device(CreationModificationMixin):
    """
    Contains devices (WW controllers).  A device may be associated with the Owner
    """
    _STATUSES = (
        ('A', 'Active'), # when everything is okay
        ('I', 'Inactive'), # when we got nothing from SPA controllers for X minutes
        ('F', 'Failure'), # when controller says it has issues
    )

    _TYPES = (
        ('S', 'Spa'),
        ('P', 'Pool'),
    )

    udid    = models.CharField(max_length=255, verbose_name="Unique ID / MAC Address", help_text="MAC Address of WiFi controller", unique=True, null=False, blank=False, db_index=True)
    type    = models.CharField(max_length=1, choices=_TYPES, null=False, blank=False)
    title   = models.CharField(max_length=255, null=False, blank=False, db_index=True)
    status  = models.CharField(max_length=1, default='A', choices=_STATUSES)
    pinged  = models.DateTimeField(null=True)
    owner   = models.ForeignKey(Owner, verbose_name="Owner", null=True, blank=True, db_index=True)

    def __str__(self):
        return self.udid

这表示将向API端点发送离散请求的硬件设备,因此我需要对每个请求进行身份验证,理想情况下使用基于令牌的标识,例如

POST /api/devices/login 
{
   udid: '...mac address...',
   hash: '...sha256...hash string',
   time: '2015-01-01 12:24:30'
}

hash将在设备端计算为sha256(salt + udid + current_time) 将在DRF侧内部/ login上计算相同的哈希值以进行比较并生成将保存在REDIS中并返回响应的令牌。

所有将来的请求都将此标记作为标头传递,将在自定义权限类中进行检查。

我的问题:

  1. 我想在请求类上设置自定义属性,例如 request.device,request.device.is_authenticated()
  2. 我应该在哪里放置此功能?

    1. 你认为我的方法有问题吗?也许建议改进?

2 个答案:

答案 0 :(得分:6)

正如@ daniel-van-flymen指出的那样,返回设备而不是用户可能不是一个好主意。所以我所做的是创建一个扩展DeviceUser的{​​{1}}类,并在我的自定义身份验证中返回(毕竟设备基本上是匿名用户)。

django.contrib.auth.models.AnonymousUser

此代码位于from myapp.models import Device from rest_framework import authentication from django.contrib.auth.models import AnonymousUser from rest_framework.exceptions import AuthenticationFailed class DeviceUser(AnonymousUser): def __init__(self, device): self.device = device @property def is_authenticated(self): return True class DeviceAuthentication(authentication.BaseAuthentication): def authenticate(self, request): udid = request.META.get("HTTP_X_UDID", None) if not udid: return None try: device = Device.objects.get(udid=udid) except Device.DoesNotExist: raise AuthenticationFailed("Invalid UDID") if not device.active: raise AuthenticationFailed("Device is inactive or deleted") request.device = device return (DeviceUser(device), None) ,您可以将以下内容添加到您的设置中:

myapp.authentication

原始规范中的一些注意事项:我已在身份验证器中修改了包含设备的请求,因此您可以执行REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ( "myapp.authentication.DeviceAuthentication", ) } ;但是,用户将是request.device.is_authenticated,因此您也可以执行DeviceUser(只要您对request.user.device.is_authenticated属性进行适当的检查)。

您的原始规范也要求实现device,并且可以将此身份验证类子类化为更直接地使用它;为简单起见,我只是让设备在其请求中包含X-UDID标头。

另请注意,与令牌身份验证机制一样,您必须将此方法与HTTPS一起使用,否则TokenAuthentication将以纯文本格式发送,允许某人模拟设备。

答案 1 :(得分:0)

您可以继承DRF的BaseAuthentication类并覆盖.authenticate(self,request)方法。 成功验证后,此功能应返回(设备,无)。这将在request.user属性中设置设备对象。 您可以在Device模型类中实现is_authenticated()。

class APICustomAuthentication(BaseAuthentication):
    ---
    def  authenticate(self, request):
        ----
        return (device, None)    # on successful authentication

将APICustomAuthentication添加到'DEFAULT_AUTHENTICATION_CLASSES' 在设置中。

有更多详情可供here