使用安全删除的用户进行 DRF 和令牌身份验证?
DRF and Token authentication with safe-deleted users?
我正在使用名为 django-safedelete 的 Django 程序包,它允许删除用户而无需将其从数据库中删除。
基本上,它为模型添加了一个delete
属性,像User.objects.all()
这样的查询不会return删除模型。
您仍然可以使用特殊管理器查询所有对象。例如 User.objects.all_with_deleted()
将 return 所有用户,包括已删除的用户。 User.objects.deleted_only()
将 return 删除的。
除一种情况外,这按预期工作。
我正在使用 Django Rest Framework 3.9 为我的用户使用令牌身份验证,并且在我的 DRF 视图中,我正在使用内置权限 IsAuthenticated
.
我正在使用的基本 CBV 代码:
class MyView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
return Response(status=HTTP_200_OK)
IsAuthenticated
权限的DRF实现代码:
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
问题
当用户被软删除后,他仍然可以使用其令牌进行身份验证。
我希望用户在被软删除时出现 401 未授权错误。
怎么了?
为什么?
如果我们查看 DRF TokenAuthentication
[source-code] 的 authenticate_credentials()
方法,我们可以看到,
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
if not token.user.is_active:
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
这表明它没有过滤掉 软删除的用户实例
解决方案?
在相应的视图中创建自定义身份验证 class 和 wire-up
# authentication.py
from rest_framework.authentication import TokenAuthentication, exceptions, _
class CustomTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
<b>if not token.user.is_active or not token.user.deleted: # Here I added something new !!</b>
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
和 wire-up 的观看次数
# views.py
from rest_framework.views import APIView
class MyView(APIView):
<b>authentication_classes = (CustomTokenAuthentication,)</b>
permission_classes = (IsAuthenticated,)
def get(self, request):
return Response(status=HTTP_200_OK)
DRF 已经使用 is_active
属性 来决定用户是否能够进行身份验证。每当您删除用户时,请务必同时将 is_active
设置为 False
。
对于django-safedelete:
由于您正在使用 django-safedelete
,因此您必须将 delete()
方法重写为 de-activate,然后使用 super()
执行原始行为,一些喜欢:
class MyUserModel(SafeDeleteModel):
_safedelete_policy = SOFT_DELETE
my_field = models.TextField()
def delete(self, *args, **kwargs):
self.is_active = False
super().delete(*args, **kwargs)
def undelete(self, *args, **kwargs):
self.is_active = True
super().undelete(*args, **kwargs)
请注意,这也适用于 QuerySets,因为 SafeDeleteModel
的管理器覆盖了 QuerySet delete()
方法。 (参见:https://github.com/makinacorpus/django-safedelete/blob/master/safedelete/queryset.py)
此解决方案的好处是您不必更改每个 APIView 的身份验证 class,以及任何依赖 is_active
属性 的应用程序 User
模型将表现理智。另外,如果您不这样做,那么您将删除同样处于活动状态的对象,因此这没有多大意义。
我正在使用名为 django-safedelete 的 Django 程序包,它允许删除用户而无需将其从数据库中删除。
基本上,它为模型添加了一个delete
属性,像User.objects.all()
这样的查询不会return删除模型。
您仍然可以使用特殊管理器查询所有对象。例如 User.objects.all_with_deleted()
将 return 所有用户,包括已删除的用户。 User.objects.deleted_only()
将 return 删除的。
除一种情况外,这按预期工作。
我正在使用 Django Rest Framework 3.9 为我的用户使用令牌身份验证,并且在我的 DRF 视图中,我正在使用内置权限 IsAuthenticated
.
我正在使用的基本 CBV 代码:
class MyView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
return Response(status=HTTP_200_OK)
IsAuthenticated
权限的DRF实现代码:
class IsAuthenticated(BasePermission):
"""
Allows access only to authenticated users.
"""
def has_permission(self, request, view):
return bool(request.user and request.user.is_authenticated)
问题
当用户被软删除后,他仍然可以使用其令牌进行身份验证。
我希望用户在被软删除时出现 401 未授权错误。 怎么了?
为什么?
如果我们查看 DRF TokenAuthentication
[source-code] 的 authenticate_credentials()
方法,我们可以看到,
def authenticate_credentials(self, key): model = self.get_model() try: token = model.objects.select_related('user').get(key=key) except model.DoesNotExist: raise exceptions.AuthenticationFailed(_('Invalid token.')) if not token.user.is_active: raise exceptions.AuthenticationFailed(_('User inactive or deleted.')) return (token.user, token)
这表明它没有过滤掉 软删除的用户实例
解决方案?
在相应的视图中创建自定义身份验证 class 和 wire-up
# authentication.py
from rest_framework.authentication import TokenAuthentication, exceptions, _
class CustomTokenAuthentication(TokenAuthentication):
def authenticate_credentials(self, key):
model = self.get_model()
try:
token = model.objects.select_related('user').get(key=key)
except model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
<b>if not token.user.is_active or not token.user.deleted: # Here I added something new !!</b>
raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))
return (token.user, token)
和 wire-up 的观看次数
# views.py
from rest_framework.views import APIView
class MyView(APIView):
<b>authentication_classes = (CustomTokenAuthentication,)</b>
permission_classes = (IsAuthenticated,)
def get(self, request):
return Response(status=HTTP_200_OK)
DRF 已经使用 is_active
属性 来决定用户是否能够进行身份验证。每当您删除用户时,请务必同时将 is_active
设置为 False
。
对于django-safedelete:
由于您正在使用 django-safedelete
,因此您必须将 delete()
方法重写为 de-activate,然后使用 super()
执行原始行为,一些喜欢:
class MyUserModel(SafeDeleteModel):
_safedelete_policy = SOFT_DELETE
my_field = models.TextField()
def delete(self, *args, **kwargs):
self.is_active = False
super().delete(*args, **kwargs)
def undelete(self, *args, **kwargs):
self.is_active = True
super().undelete(*args, **kwargs)
请注意,这也适用于 QuerySets,因为 SafeDeleteModel
的管理器覆盖了 QuerySet delete()
方法。 (参见:https://github.com/makinacorpus/django-safedelete/blob/master/safedelete/queryset.py)
此解决方案的好处是您不必更改每个 APIView 的身份验证 class,以及任何依赖 is_active
属性 的应用程序 User
模型将表现理智。另外,如果您不这样做,那么您将删除同样处于活动状态的对象,因此这没有多大意义。