简体   繁体   English

Django Rest Framework 自定义身份验证

[英]Django Rest Framework custom authentication

Django rest framework: Custom Authentication Django rest 框架:自定义身份验证

I want to use custom authentication in my Django app but cannot find how to apply this.我想在我的 Django 应用程序中使用自定义身份验证,但找不到如何应用它。 The example given in the documentation is clear to me but they did not mention where to create this new class and how to use this.文档中给出的示例对我来说很清楚,但他们没有提到在哪里创建这个新类以及如何使用它。

How to implement a custom authentication scheme in DRF?如何在 DRF 中实现自定义身份验证方案?

To implement a custom authentication scheme, we need to subclass the DRF's BaseAuthentication class and override the .authenticate(self, request) method.要实现自定义身份验证方案,我们需要BaseAuthentication DRF 的BaseAuthentication类并覆盖.authenticate(self, request)方法。

The method should return a two-tuple of (user, auth) if authentication succeeds, or None otherwise.如果身份验证成功,该方法应返回(user, auth)的二元组,否则返回None In some circumstances, we may raise an AuthenticationFailed exception from the .authenticate() method.在某些情况下,我们可能会从.authenticate()方法中引发AuthenticationFailed异常。

Example (from DRF docs ):示例(来自DRF 文档):

Lets say we want to authenticate any incoming request as the user given by the username in a custom request header named 'X_USERNAME' .假设我们希望将任何传入请求作为名为'X_USERNAME'的自定义请求标头中的username指定的用户进行身份验证。

Step-1: Create the Custom authentication class步骤 1:创建自定义身份验证类

To do that, we will create an authentication.py file in my_app .为此,我们将在my_app创建一个authentication.py文件。

# my_app/authentication.py
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME') # get the username request header
        if not username: # no username passed in request headers
            return None # authentication did not succeed

        try:
            user = User.objects.get(username=username) # get the user
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user') # raise exception if user does not exist 

        return (user, None) # authentication successful

Step-2: Specify the custom authentication class步骤 2:指定自定义身份验证类

After creating the custom authentication class, we need to define this authentication class in our DRF settings.创建自定义身份验证类后,我们需要在我们的 DRF 设置中定义此身份验证类。 Doing this, all the requests will be authenticated based on this authentication scheme.这样做,所有请求都将根据此身份验证方案进行身份验证。

'DEFAULT_AUTHENTICATION_CLASSES': (       
    'my_app.authentication.ExampleAuthentication', # custom authentication class
    ...
),

Note: If you want to use this custom authentication class on per-view basis or per-viewset basis and not on global level, you can define this authentication class explicitly in your views.注意:如果您想基于每个视图或每个视图集而不是全局级别使用此自定义身份验证类,则可以在视图中明确定义此身份验证类。

class MyView(APIView):

    authentication_classes = (ExampleAuthentication,) # specify this authentication class in your view

    ...

Below is a simple example that can be used to achieve a custom authentication.下面是一个可用于实现自定义身份验证的简单示例。 To access the endpoint you have to pass the username&password on the POST data.要访问端点,您必须在 POST 数据上传递用户名和密码。

urls.py网址.py

urlpatterns = [
    url(r'^stuff/', views.MyView.as_view()),
    ...
]

views.py视图.py

from rest_framework.response import Response
from rest_framework.views import APIView    
from rest_framework.permissions import IsAuthenticated
from rest_framework import exceptions
from rest_framework import authentication
from django.contrib.auth import authenticate, get_user_model
from rest_framework.authentication import SessionAuthentication


class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        # Get the username and password
        username = request.data.get('username', None)
        password = request.data.get('password', None)

        if not username or not password:
            raise exceptions.AuthenticationFailed(_('No credentials provided.'))

        credentials = {
            get_user_model().USERNAME_FIELD: username,
            'password': password
        }

        user = authenticate(**credentials)

        if user is None:
            raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

        if not user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

    return (user, None)  # authentication successful


class MyView(APIView):
    authentication_classes = (SessionAuthentication, ExampleAuthentication,)
    permission_classes = (IsAuthenticated,)

    def post(self, request, format=None):    
        content = {
            'user': unicode(request.user),
            'auth': unicode(request.auth),  # None
        }
        return Response(content)

Curl卷曲

curl -v -X POST http://localhost:8000/stuff/ -d 'username=my_username&password=my_password'

I used the following way我使用了以下方式

from rest_framework_jwt.settings import api_settings
from rest_framework import status, generics

 class UserLogin(generics.CreateAPIView):

    def post(self, request, *args, **kwargs):
        email = request.data['email']
        if email is None:
            return Response({'error': 'Email not informed'}, status=status.HTTP_403_FORBIDDEN)
        try:
            user = User.objects.get(email=email)
            if not user.check_password(request.data['password']):
                return Response({'error': 'Email ou senha incorreto'}, status=status.HTTP_400_BAD_REQUEST)
            jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
            jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({"token": token, "user":UserSessionSerializerAuth(user,
                        context={'request': request}).data}, status=status.HTTP_200_OK)
        except User.DoesNotExist:
            return Response({'error': 'User not found'}, status=status.HTTP_403_FORBIDDEN)

In the folder that holds your api files, create another file to hold your custom authentication class, such as authentication.py .在保存 api 文件的文件夹中,创建另一个文件来保存您的自定义身份验证类,例如authentication.py Then in your settings, under DEFAULT_AUTHENTICATION_CLASSES , point to your custom authentication class.然后在您的设置中,在DEFAULT_AUTHENTICATION_CLASSES下,指向您的自定义身份验证类。

I faced similar situation where I had to implement a custom authentication class.我遇到了类似的情况,我必须实现自定义身份验证类。 Request coming to the rest endpoint were using basic auth but the username weren't a django user.到达其余端点的请求正在使用basic auth但用户名不是 django 用户。 Requests were authnticated against username and password configured in settings.py .要求被authnticated对用户名和密码在配置settings.py Here's my implementation:这是我的实现:

- Created custom authentication class - 创建自定义身份验证类

# myapp/api/auth.py

"""Custom authentication module"""

import base64
import binascii

from django.conf import settings
from django.utils.six import text_type
from django.utils.translation import ugettext_lazy as _

from rest_framework.authentication import BaseAuthentication
from rest_framework import HTTP_HEADER_ENCODING, exceptions


class CustomAuthentication(BaseAuthentication):
    """
    Custom authentication class.
    It will authenticate any incoming request
    as the user given by the username in a
    custom request header.
    """

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """

        # Gets authorization from request header
        # and checks different possibility of
        # invalid header.
        # ======================================

        auth = self.get_authorization_header(request).split()

        if not auth or auth[0].lower() != b"basic":
            raise exceptions.AuthenticationFailed(_("Invalid header!"))

        if len(auth) == 1:
            msg = _("Invalid basic header. No credentials provided.")
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _(
                "Invalid basic header. Credentials string should not contain spaces."
            )
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = (
                base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(":")
            )
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _("Invalid basic header. Credentials not correctly base64 encoded.")
            raise exceptions.AuthenticationFailed(msg)

        # parses username and password.
        userid, password = auth_parts[0], auth_parts[2]

        if userid != settings.FERRATUM_CALLBACK_USERNAME:
            msg = _("Invalid basic header. Username is incorrect!")
            raise exceptions.AuthenticationFailed(msg)

        if password != settings.FERRATUM_CALLBACK_PASSWORD:
            msg = _("Invalid basic header. Password is incorrect!")
            raise exceptions.AuthenticationFailed(msg)

        # An user object is expected to be returned
        # in case of successful authentication. Therefore
        # a user object is returned with the give username
        # in the header. This user doesn't exists in the
        # django user User model.
        # ===============================================

        user = {
            "username": userid,
            "password": "",
            "email": "",
            "first_name": "",
            "last_name": "",
            "company": "",
        }

        return (user, None)

    @staticmethod
    def get_authorization_header(request):
        """
        Return request's 'Authorization:' header, as a bytestring.

        Hide some test client ickyness where the header can be unicode.
        """

        auth = request.META.get("HTTP_AUTHORIZATION", b"")
        if isinstance(auth, text_type):
            # Work around django test client oddness
            auth = auth.encode(HTTP_HEADER_ENCODING)
        return auth

- Added it in DEFAULT_AUTHENTICATION_CLASS - 在 DEFAULT_AUTHENTICATION_CLASS 中添加它

# myapp/settings.py

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.SessionAuthentication",
        "mozilla_django_oidc.contrib.drf.OIDCAuthentication",
        "rest_framework.authentication.BasicAuthentication",
        "users.auth.SaveriumTokenAuthentication",
        "api.auth.CustomAuthentication"
    ),
    'DEFAULT_RENDERER_CLASSES': DEFAULT_RENDERER_CLASSES
}

- Used the custom authentication in view - 在视图中使用自定义身份验证

# myapp/api/view/api_view.py

from api.auth import CustomAuthentication


class UpdateStatus(APIView):
    """
    It updates application status
    """
    
    # Custom authentication scheme.
    authentication_classes = [CustomAuthentication]

    def post(self, *args, **kwargs):
        """
        Callback comes as a POST request
        with data in JSON format.
        """

        data = self.request.data
        ...

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

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