简体   繁体   English

在 Django Rest 框架中使用 Tokenauthentication 进行身份验证时,last_login 字段未更新

[英]last_login field is not updated when authenticating using Tokenauthentication in Django Rest Framework

I'm working in a project which relies in a Django User model and TokenAuthentication under DjangoRestFramework我正在一个项目中工作,该项目依赖于 DjangoRestFramework 下的 Django 用户 model 和 TokenAuthentication

I was requested to get last login datetime for each user and I've realized that this field is not getting updated when I call the authentication REST endpoint.我被要求获取每个用户的上次登录日期时间,并且我意识到当我调用身份验证 REST 端点时,该字段没有得到更新。

Is this a known fact?这是众所周知的事实吗? Am I missing something I must do in order to get that field updated each time the token authentication is called?为了在每次调用令牌身份验证时更新该字段,我是否遗漏了一些我必须做的事情?

Thanks谢谢

Well, at the end I inherited from the REST Framework TokenAuthentication, pointing to it in the urls file好吧,最后我继承自 REST Framework TokenAuthentication,在 urls 文件中指向它

url(r'^api-token-auth/', back_views.TokenAuthenticationView.as_view()),

and the View handles the request and manually calls the update_last_login like this:视图处理请求并手动调用 update_last_login ,如下所示:

from django.contrib.auth.models import update_last_login

class TokenAuthenticationView(ObtainAuthToken):
    """Implementation of ObtainAuthToken with last_login update"""

    def post(self, request):
        result = super(TokenAuthenticationView, self).post(request)
        try:
            request_user, data = requests.get_parameters(request)
            user = requests.get_user_by_username(data['username'])
            update_last_login(None, user)            
        except Exception as exc:
            return None
        return result

@FDF answer is great. @FDF 的回答很棒。 Here is another way of doing it.这是另一种方法。

We send user_logged_in signals that will call update_last_login :我们发送将调用update_last_login user_logged_in信号:

user_logged_in.send(sender=user.__class__, request=request, user=user)

Here is a working view (based on a custom User model that uses email as USERNAME_FIELD) :这是一个工作视图(基于使用电子邮件作为 USERNAME_FIELD 的自定义用户模型):

from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.views import APIView

from django.contrib.auth.signals import user_logged_in
from emailauth.serializers import AuthTokenSerializer, UserSerializer


class ObtainAuthToken(APIView):
    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = AuthTokenSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        user_logged_in.send(sender=user.__class__, request=request, user=user)
        return Response({'token': token.key, 'user': UserSerializer(user).data})


obtain_auth_token = ObtainAuthToken.as_view()

You can find the full source code here : Api View with last_login updated您可以在此处找到完整的源代码: 更新了 last_login 的 Api View

Hope this helps.希望这可以帮助。

A cleaner way to do it:一种更清洁的方法:

from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken

class LoginToken(ObtainAuthToken):
    def post(self, request, *args, **kwargs):
        result = super().post(request, *args, **kwargs)
        token = Token.objects.get(key=result.data['token'])
        update_last_login(None, token.user)
        return result

And then setup urls.py:然后设置 urls.py:

url(r'^api-token-auth/', views.LoginToken.as_view()),

My answer for Django==2.0.5 , django-rest-framework-social-oauth2==1.1.0我对Django==2.0.5回答, django-rest-framework-social-oauth2==1.1.0

from django.contrib.auth import user_logged_in
from oauth2_provider.models import AccessToken
from rest_framework import status
from rest_framework_social_oauth2.views import TokenView

class MyTokenView(TokenView):
    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)
        if response.status_code == status.HTTP_200_OK:
            token = AccessToken.objects.get(token=response.data['access_token'])
            user = token.user
            user_logged_in.send(sender=type(user), request=request, user=user)
        return response

urls.py:网址.py:

from django.urls import path

urlpatterns = [
    path('token', MyTokenView.as_view(), name='token'),
]

Here is my solution using a ViewSet:这是我使用 ViewSet 的解决方案:

views.py:视图.py:

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework import viewsets
from django.contrib.auth.models import update_last_login

class LoginViewSet(viewsets.ViewSet):
    """Checks email and password and returns an auth token."""

    serializer_class = AuthTokenSerializer

    def create(self, request):
        """Use the ObtainAuthToken APIView to validate and create a token."""

        ##update last_login
        try:
            user = models.User.objects.get(email = request.data['username'])
            update_last_login(None, user)
        except:
            pass

        return ObtainAuthToken().post(request)

Now just add this viewset to the urls.py:现在只需将此视图集添加到 urls.py:

router.register('login', views.LoginViewSet, base_name="login")

This is the newest code for django 3.0.8.这是 django 3.0.8 的最新代码。
:) :)
thx FDF!谢谢 FDF!

from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.views import ObtainAuthToken
from django.contrib.auth import get_user_model


class TokenAuthenticationView(ObtainAuthToken):
    """Implementation of ObtainAuthToken with last_login update"""
    
    def post(self, request):
        result = super(TokenAuthenticationView, self).post(request)
        currentUserModel = get_user_model()
        try:
            user = currentUserModel.objects.get(username=request.data['username'])
            update_last_login(None, user)
        except Exception as exc:
            return None
        return result

impl via signals通过信号实现

from django.dispatch import receiver
from django.db.models.signals import post_save
from django.contrib.auth import user_logged_in

from oauth2_provider.models import AccessToken


@receiver(post_save, sender=AccessToken)
def post_save_access_token(instance, created, raw, **kwargs):
    if not created or raw:
        return
    user_logged_in.send(sender=instance.user.__class__,  user=instance.user)

for anyone using knox to authenticate the user then need to edit api.py file and import user_logged_in like below对于使用 knox 对用户进行身份验证的任何人,则需要编辑 api.py 文件并导入 user_logged_in,如下所示

from django.contrib.auth.signals import user_logged_in

after that in LoginAPI class in the same api.py file add the below line after _, token = AuthToken.objects.create(user) like below之后,在同一 api.py 文件中的 LoginAPI 类中,在 _, token = AuthToken.objects.create(user) 之后添加以下行,如下所示

user_logged_in.send(sender=user.__class__, request=request, user=user)

If you're using Simple JWT for Django, you can do it pretty much easily by following this link .如果您将 Simple JWT 用于 Django,您可以通过以下链接轻松完成。 You just have to add你只需要添加

SIMPLE_JWT = {
...,
'UPDATE_LAST_LOGIN': True,
...,
}

Sometimes you don't want exactly the login time, since front-end stores the token and use it without logging in again.有时您不想要确切的登录时间,因为前端存储令牌并使用它而无需再次登录。

You can save the current time in every request that user is authenticated and authorized by subclassing APIView from rest_framework您可以通过从rest_framework APIView来保存用户通过身份验证和授权的每个请求中的当前时间

from django.contrib.auth.models import update_last_login
from rest_framework.views import APIView

class CustomAPIView(APIView):
    def check_permissions(self, request):
        super().check_permissions(request)
        # here user is authorized (otherwise an exception would have been raised)
        update_last_login(None, request.user)

class my_endpoint(CustomAPIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, company_id=None):
        ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM