与Django Rest Framework的非用户连接的自定义身份验证 [英] Custom Authentication for non-user connection with Django Rest Framework

查看:159
本文介绍了与Django Rest Framework的非用户连接的自定义身份验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已使用TokenAuthentication启用了DRF的用户身份验证

I have enabled user authentication with DRF using TokenAuthentication

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端点发送离散请求的硬件设备,因此我需要对每个请求进行身份验证,理想情况下,需要使用基于令牌的标识,例如

This represents hardware device that will be sending discrete requests to API endpoints, therefore I need to authenticate each request and ideally with token based identification, like

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

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

hash will be calculated on device side as sha256(salt + udid + current_time) the same hash will be calculated on DRF side inside /login to compare and generate token that will be saved in REDIS and returned back with response.

将来所有的请求都将将此令牌作为标头传递,这将在自定义Permission类中进行检查.

All future requests will be passing this token as a header, which will be checked in custom Permission class.

我的问题:

  1. 我想在请求类上设置自定义属性,例如 request.device,request.device.is_authenticated()
  1. I'd like to set a custom property on request class, like request.device, request.device.is_authenticated()

该功能应该放在哪里?

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

推荐答案

正如@ daniel-van-flymen指出的那样,返回设备而不是用户可能不是一个好主意.因此,我要做的是创建一个扩展django.contrib.auth.models.AnonymousUserDeviceUser类,并在我的自定义身份验证中返回该类(毕竟,设备本质上是匿名用户).

As @daniel-van-flymen pointed out, it's probably not a good idea to return a device instead of a user. So what I did was create a DeviceUser class that extends django.contrib.auth.models.AnonymousUserand return that in my custom authentication (devices are essentially anonymous users, after all).

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中,然后您可以将以下内容添加到设置中:

This code lives in myapp.authentication, you can then add the following to your settings:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "myapp.authentication.DeviceAuthentication", 
    )
}

您原始规格的一些注意事项:我已经修改了身份验证器中的请求以包括设备,因此您可以执行request.device.is_authenticated;但是,用户将是DeviceUser,因此您也可以执行request.user.device.is_authenticated(只要您对device属性进行了适当的检查).

A couple of notes from your original spec: I've modified the request in the authenticator to include the device, so you can do request.device.is_authenticated; however, the user will be a DeviceUser so you could also do request.user.device.is_authenticated (so long as you do the appropriate checks for the device attribute).

您的原始规范也要求实现TokenAuthentication,可以对该认证类进行子类化,以便更直接地使用它.为了简单起见,我只是让设备在其请求中包含X-UDID标头.

Your original spec also asked to implement TokenAuthentication, and it is possible to subclass this authentication class to use it more directly; for simplicity, I'm just having the device include the X-UDID header in their request.

还请注意,与令牌身份验证机制一样,您必须将此方法与HTTPS一起使用,否则UDID将以纯文本形式发送,从而允许他人模拟设备.

Also note that as with the token authentication mechanism, you must use this method with HTTPS, otherwise the UDID will be sent in plain text, allowing someone to impersonate a device.

这篇关于与Django Rest Framework的非用户连接的自定义身份验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆