与Django Rest Framework的非用户连接的自定义身份验证 [英] Custom Authentication for non-user connection with 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.
我的问题:
- 我想在请求类上设置自定义属性,例如 request.device,request.device.is_authenticated()
- I'd like to set a custom property on request class, like request.device, request.device.is_authenticated()
该功能应该放在哪里?
- 您认为我的做法有问题吗?也许有改进的建议?
推荐答案
正如@ daniel-van-flymen指出的那样,返回设备而不是用户可能不是一个好主意.因此,我要做的是创建一个扩展django.contrib.auth.models.AnonymousUser
的DeviceUser
类,并在我的自定义身份验证中返回该类(毕竟,设备本质上是匿名用户).
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.AnonymousUser
and 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屋!