【问题标题】:Token Authentication Implementation in Django Rest FrameworkDjango Rest Framework 中的令牌认证实现
【发布时间】:2018-08-08 19:40:49
【问题描述】:

我正在使用 Django Rest 框架开发 API 后端。我最初使用会话身份验证开发它,不知道它不能用于发送到移动应用程序。我在 Postman 中尝试用户登录时遇到了 CSRF 保护方面的问题。

现在,由于我必须转向基于令牌的身份验证才能使其正常工作,我该怎么做呢?我想知道如何快速实施它。我已经浏览了有关 * 的教程和答案,但无法在实践中实现这一点

另外,令牌认证是最适合的认证方法吗?我应该使用默认提供的 DRF 模块或 JWT 还是其他一些实现?我可以只对用户登录使用令牌身份验证,而对其他 3 个 API 使用会话身份验证吗?

class UserLogin(APIView):
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request, format='json'):
    username = request.POST.get('username')
    email = request.POST.get('email')
    password = request.POST.get('password')
    user = EmailBackend.authenticate(self,username = email, password = password)
    if user:
        id = user.id
        return Response(id, status=status.HTTP_201_CREATED)
    else:
        return Response("Failure", status=HTTP_400_BAD_REQUEST)

class UserRegistration(APIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    def post(self, request, format='json'):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
             user = serializer.save()
             if user:
             return Response('Success', status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)

class RecommendationQuestions(generics.ListCreateAPIView):
    def post(self, request, format = 'json'):
        """Save the post data when logging in."""
        uid = request.data['user_id']
        resp_list = MovieSerializer.provide_movie_choices(uid)
        return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)

class RecommendationGenerator(generics.ListCreateAPIView):
    queryset = Ratings.objects.all()#.filter(id__in=(1,2))
    serializer_class= RatingsSerializer#(queryset,many=True)
    def post(self, request, format='json'):
        many = isinstance(request.data, list)
        serializer = RatingsSerializer(data = request.data, many = many)
        x = 0
        if serializer.is_valid():
            uid = [d['userId'] for d in serializer.data]
            resp_list = RatingsSerializer.generate_recommendations(self, uid[0])
            return Response(resp_list, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors,status=status.HTTP_400_BAD_REQUEST)

这是 API 的 views.py。

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    令牌认证设置

    您可以通过包含来启用 TokenAuthentication

    'rest_framework.authtoken'
    

    在 INSTALLED_APPS 设置中 (documentation)。

    之后你必须运行migrate。运行迁移后,您需要为您的用户创建令牌。这是一个执行此操作的示例代码:

    from django.contrib.auth.models import User
    from rest_framework.authtoken.models import Token
    
    users = User.objects.all()
    for user in users:
        token, created = Token.objects.get_or_create(user=user)
    

    你只运行一次。此外,您需要为每个新用户创建令牌。您可以使用 post_save 信号自动执行此操作:

    from django.contrib.auth.models import User
    from django.dispatch import receiver
    from django.db.models.signals import post_save
    from rest_framework.authtoken.models import Token
    
    @receiver(post_save, sender=User)
    def create_auth_token(sender, instance=None, created=False, **kwargs):
        if created:
            Token.objects.create(user=instance) 
    

    此外,您必须通过包含来添加配置身份验证类

    'rest_framework.authentication.TokenAuthentication'
    

    在您的设置中'DEFAULT_AUTHENTICATION_CLASSES' (documentation)

    您需要做的最后一件事是将用于令牌身份验证的 url 添加到您的urls.py

    from rest_framework.authtoken import views as drf_views
    
    urlpatterns += [
        path('api-token-auth/', drf_views.obtain_auth_token)
    ]
    

    基于会话的身份验证用于使用浏览器登录 API。基于令牌的身份验证是无状态的,这意味着服务器不会在服务器上存储有关客户端会话的任何状态。阅读更多关于 here 的差异。如果您通过基于 Token 的身份验证登录,您将没有会话,并且无法通过令牌以外的任何其他方式访问 API。

    认证示例

    下面是使用requests库在Python中进行令牌认证的示例代码。

    # Authentication
    import requests
    r = requests.post(<add your token auth url here>, data={'username': 'my_username', 'password': 'my_password'})
    if r.status_code == 200:
        response = r.json()
        token = response['token']
        print(token)
    

    令牌必须用于所有其他 API 请求。它是通过标头发送的。

    # Consume API
    import requests
    headers = {'Authorization': 'Token {}'.format(<your token here>)}
    # Request method is either GET, POST, PUT, PATCH or DELETE
    r = requests.request(method=<request method>, url=<url to api>, headers=headers)
    
    # or you can also use
    # requests.get(url=<url to api>, headers=headers) or
    # requests.post(url=<url to api>, headers=headers, data=<your data>) etc.
    

    【讨论】:

    • 感谢@Borut 的解释。我在邮递员上遇到“未提供身份验证凭据”错误。然后我在我的 INSTALLED_APPS 中添加“rest_framework.authentication.TokenAuthentication”。问题已解决。
    【解决方案2】:

    我会推荐你​​使用 JWT,它比提供的 rest_framework.authtoken 更安全。比如一对令牌/刷新令牌来设置你的主令牌小的过期时间。这减少了令牌被盗或损坏的机会。同样在您的 JWT 令牌中,您可以存储在许多情况下非常有用的有效负载。

    有一个非常好的 DRF 库,它实现了将 JWT 与 DRF 结合使用的所有方面,并且非常灵活地适应您的目的。

    http://getblimp.github.io/django-rest-framework-jwt/

    我可以只对用户登录使用令牌身份验证,而对其他 3 个 API 使用会话身份验证吗?

    是的,你绝对可以。 APIView 的每个实例都有属性“authentication_classes”,您可以专门为您想要的 API 设置 SessionAuthentication。

    例如:

    class RecommendationQuestions(generics.ListCreateAPIView):
            authentication_classes = (SessionAuthentication, )
    
            def post(self, request, format = 'json'):
                """Save the post data when logging in."""
                uid = request.data['user_id']
                resp_list = MovieSerializer.provide_movie_choices(uid)
                return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)
    

    也可以同时使用

    class RecommendationQuestions(generics.ListCreateAPIView):
            authentication_classes = (SessionAuthentication, JSONWebTokenAuthentication)
    
            def post(self, request, format = 'json'):
                """Save the post data when logging in."""
                uid = request.data['user_id']
                resp_list = MovieSerializer.provide_movie_choices(uid)
                return Response(resp_list, status=status.HTTP_400_BAD_REQUEST)
    

    【讨论】: