[PYTHON] Implementieren Sie die JWT-Anmeldefunktion im Django REST-Framework

Artikel bis zum letzten Mal

  1. Django REST Framework + Vue.js "Machen wir eine EC-Site wie SEIYU" 6 Mal (geplant)
  2. Erstellen einer EC-Site im SEIYU-Stil (1) Anforderungsanalyse und Projektinitialisierung
  3. Erstellen Sie eine EC-Site im SEIYU-Stil (2) Erneuern Sie den Verwaltungsbildschirm mit Xadmin
  4. Erstellen wir eine SEIYU-ähnliche EC-Site (3) Erstellen wir eine explosive Geschwindigkeits-API mit dem Django REST-Framework
  5. Erstellen einer EC-Site im SEIYU-Stil (4) Verwenden Sie Vue.js Projektinitialisierung TypeScript

Auch wenn Sie sich den Inhalt bisher nicht ansehen, können Sie den Inhalt dieser Zeit verstehen. Ich hoffe, Sie können bis zum Ende mit mir in Kontakt bleiben. : entspannt:

Was ist JWT?

Dies ist eine Abkürzung für JSON Web Token. Es ist möglich, beliebige Informationen (Ansprüche) im Token zu speichern. Beispielsweise generiert der Server ein Token, das die Informationen "Als Administrator angemeldet" für den Client enthält. in der Lage sein. Der Client kann das Token verwenden, um zu beweisen, dass er als Administrator angemeldet ist. - [Wikipedia] (https://ja.wikipedia.org/wiki/JSON_Web_Token)

Von Hand umgesetzt

Sie können die JWT-Anmeldung einfach mithilfe der Bibliothek django-rest-framework-jwt implementieren. Es ist einfacher, auf verschiedene Arten anzupassen, wenn Sie es direkt schreiben, also schreiben Sie es direkt.

Erstellen Sie ein neues Django REST-Framework-Projekt

pip install Django
pip install djangorestframework
pip install markdown
pip install django-filter
django-admin startproject jwttest
cd jwttest
python manage.py runserver

Wenn Sie den Server starten und die Rakete normal sehen, ist dies in Ordnung.

Grundeinstellung

Erstellen Sie eine neue App.

python manage.py startapp api

Fügen Sie es zusammen mit "rest_framework" zu "INSTALLED_APPS" hinzu.

jwttest/settings.py


INSTALLED_APPS = [
    ...
    'rest_framework',
    'api'
]

Erstellen Sie ein Benutzermodell für die Anmeldung.

jwtest/api/models.py


from django.db import models


class UserInfo(models.Model):
    username = models.CharField(max_length=50, unique=True, db_index=True)
    password = models.CharField(max_length=100, db_index=True)
    info = models.CharField(max_length=200)

Führen Sie die DB-Migration aus.

python manage.py makemigrations
python manage.py migrate

Anmeldefunktion implementieren

Installieren Sie die Bibliothek für die JWT-Token-Generierung.

pip install pyjwt

Fügen Sie eine Klasse für die Anmeldung hinzu, Fügen Sie den Ordner "utls" unter dem Verzeichnis "api" hinzu und fügen Sie eine neue Datei "auth.py" hinzu.

api/utils/auth.py


import time
import jwt
from jwttest.settings import SECRET_KEY
from rest_framework.authentication import BaseAuthentication
from rest_framework import exceptions

from api.models import UserInfo

class NormalAuthentication(BaseAuthentication):
    def authenticate(self, request):
        username = request._request.POST.get("username")
        password = request._request.POST.get("password")
        user_obj = UserInfo.objects.filter(username=username).first()
        if not user_obj:
            raise exceptions.AuthenticationFailed('Authentifizierungsfehler')
        elif user_obj.password != password:
            raise exceptions.AuthenticationFailed('Ich habe kein Passwort')
        token = generate_jwt(user_obj)
        return (token, None)

    def authenticate_header(self, request):
        pass

#Generieren Sie ein Token mit der soeben installierten JWT-Bibliothek
#Der Inhalt des Tokens enthält Benutzerinformationen und Zeitüberschreitung
#Es wurde behoben, dass der Timeout-Schlüssel exp ist
#Dokument: https://pyjwt.readthedocs.io/en/latest/usage.html?highlight=exp
def generate_jwt(user):
    timestamp = int(time.time()) + 60*60*24*7
    return jwt.encode(
        {"userid": user.pk, "username": user.username, "info": user.info, "exp": timestamp},
        SECRET_KEY).decode("utf-8")

Fügen Sie auch eine Ansicht für die Anmeldung hinzu. Wenn die Anmeldung erfolgreich ist, wird JWT zurückgegeben. Fügen Sie die zuvor erstellte "NormalAuthentication" zu "authentication_classes" hinzu.

api/views.py


from rest_framework.views import APIView
from rest_framework.response import Response

from .utils.auth import NormalAuthentication


class Login(APIView):

    authentication_classes = [NormalAuthentication,]

    def post(self, request, *args, **kwargs):
        return Response({"token": request.user})

URL hinzufügen.

jwttest/urls.py


...
from api.views import Login

urlpatterns = [
    ...
    path('login/', Login.as_view()),
]

Fügen Sie eine Benutzerinformation hinzu![B2DC6C55-949F-4FB9-ADCB-ED3C0F894B1E_4_5005_c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320164/9608aed2 -dfb2-7b90-bce8-5bf6bc953926.jpeg)

Starten Sie den Server und versuchen Sie, sich anzumelden. 82BFEFD1-D012-4F3A-80D8-5CC828590481.jpeg Lassen Sie uns das zurückgegebene JWT mit https: // jwt.io / analysieren. BE755CEB-725D-4833-A954-0300EF1A4E0B.jpeg Das JWT enthält die von Ihnen angegebenen Informationen. Erstellen Sie eine Ansicht, die Sie nur sehen können, wenn Sie angemeldet sind, und verwenden Sie dieses Token, um darauf zuzugreifen. Fügen Sie zunächst die Authentifizierungsklasse für JWT zu "api / utils / auth.py" hinzu.

api/utils/auth.py


...
class JWTAuthentication(BaseAuthentication):
    keyword = 'JWT'
    model = None

    def authenticate(self, request):
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != self.keyword.lower().encode():
            return None

        if len(auth) == 1:
            msg = "Autorisierung deaktiviert"
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = "Autorisierung Kein ungültiger Speicherplatz"
            raise exceptions.AuthenticationFailed(msg)

        try:
            jwt_token = auth[1]
            jwt_info = jwt.decode(jwt_token, SECRET_KEY)
            userid = jwt_info.get("userid")
            try:
                user = UserInfo.objects.get(pk=userid)
                user.is_authenticated = True
                return (user, jwt_token)
            except:
                msg = "Benutzer existiert nicht"
                raise exceptions.AuthenticationFailed(msg)
        except jwt.ExpiredSignatureError:
            msg = "Zeitüberschreitung des Tokens"
            raise exceptions.AuthenticationFailed(msg)

    def authenticate_header(self, request):
        pass
...

Es wurden Ansichten zu api / views.py hinzugefügt, auf die ohne Anmeldung nicht zugegriffen werden kann.

api/views.py


...
from rest_framework.permissions import IsAuthenticated
...
class Something(APIView):
    authentication_classes = [JWTAuthentication, ]
    #Machen Sie es nur angemeldeten Benutzern zugänglich.
    permission_classes = [IsAuthenticated, ]

    def get(self, request, *args, **kwargs):
        return Response({"data": "Es ist der Inhalt"})
...

Ich werde versuchen, darauf zuzugreifen, nachdem ich die URL hinzugefügt habe.

python:jwttest:urls.py


    path('data/', Something.as_view())

Erstens Zugriff ohne Token. Es wurde zurückgegeben, dass keine Authentifizierungsdaten angegeben wurden. FFA8B8F1-6250-40FF-9FA9-6DDEB186B3EB_4_5005_c.jpeg

Nachdem ich das Token hinzugefügt hatte, konnte ich darauf zugreifen. 528923C5-B32E-44B4-A33F-2ED3F4564042_4_5005_c.jpeg

Das war's, aber ich möchte eine Analyse des Login-bezogenen Quellcodes des Django REST-Frameworks hinzufügen. Bitte lesen Sie, wenn Sie interessiert sind. : entspannt:

DRF-Login-Quellcode-Analyse

Verknüpfung mit der zu verwendenden Authentifizierungsklasse

Ich werde es basierend auf dem Code analysieren, den ich zuvor geschrieben habe. CBV (Klassenbasierte Ansichten) Wenn verwendet, wird "Versand" ausgeführt. Mit diesem als Eingang werden wir uns den Quellcode ansehen. Fügen Sie der Betrachtungsklasse, die ich zuvor geschrieben habe, "self.dispatch ()" hinzu, um es zu betrachten.

api/view.py


...
class Login(APIView):
    authentication_classes = [NormalAuthentication, ]
    def post(self, request, *args, **kwargs):
        #Fügen Sie den Quellcode hinzu und folgen Sie ihm
        #Bei Verwendung von PyCharm
        #Befehl auf dem Mac+klicken
        #Alt im Gewinn+Sollte ein Klick sein
        self.dispatch() 
        return Response({"token": request.user})
...

Das Ziel ist "def dispatch (self, request, * args, ** kwargs)" in Zeile 481 von "rest_framework / views.py". Werfen wir einen Blick auf den Inhalt der Funktion "initialize_request" in Zeile 488.

rest_framework/views.py


    def dispatch(self, request, *args, **kwargs):
        ...
        #Schauen wir uns den Inhalt dieser Funktion an
        request = self.initialize_request(request, *args, **kwargs)
        self.request = request

Die Definition des Inhalts befindet sich in Zeile 381 von "rest_framework / views.py".

rest_framework/views.py


    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
        parser_context = self.get_parser_context(request)

        return Request(
            request,
            parsers=self.get_parsers(),
            #Hier
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )

Und wenn Sie sich den Inhalt von "self.get_authenticators ()" in Zeile 390 ansehen, Ich habe den folgenden Code, der aus "self.authentication_classes" stammt. Was ist die Authentifizierungsklasse unter Verwendung der entsprechenden CBV?

rest_framework/views.py


    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]

Wenn Sie der Definition von "self.authentication_classes" folgen, Zeile 109 von rest_framework / views.py hat die folgende Definition:

rest_framework/views.py


class APIView(View):
   ...
   authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES

Daher gibt es zwei Stellen, an denen Sie direkt definieren können, welche Authentifizierungsklasse verwendet werden soll.

  1. Innerhalb der von "APIView" geerbten "CBV" -Klasse

api/views.py


...
class Login(APIView):
    #Hier
    authentication_classes = [NormalAuthentication, ]

    def post(self, request, *args, **kwargs):
        return Response({"token": request.user})
...
  1. Innerhalb der Einstellungen für REST_FRAMEWORK in settings.py
REST_FRAMEWORK = {
  'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.auth.NormalAuthentication']
}

Nachdem Sie die Beziehung zwischen CBV und der Authentifizierungsklasse verstanden haben, schauen wir uns die Funktionen der Authentifizierungsklasse an. Wir folgen auch aus "dispatch", "self.initial (request, * args, ** kwargs)" in Zeile 493 von "rest_framework / views.py".

rest_framework/views.py


    def dispatch(self, request, *args, **kwargs):
        ...

        try:
            self.initial(request, *args, **kwargs)
        ...

Ich werde der Definition folgen. Zeile 395 von rest_framework / views.py. Es gibt perform_authentication, wir werden weiter gehen.

python:rest_framework.py/views.py


...
    def initial(self, request, *args, **kwargs):
        ...
        self.perform_authentication(request)
        ...

Darüber hinaus ist request.user.

rest_framework/views.py


...
    def perform_authentication(self, request):
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        request.user
...

Seine Definition befindet sich in Zeile 213 von "rest_framework / request.py".

rest_framework/request.py


...
    @property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
...

Werfen wir einen Blick auf die Definition von "self._authenticate ()". Zeile 366 von rest_framework / request.py. Der Inhalt besteht darin, die Klasse aus der Liste der CBV-Authentifizierungsklassen zu extrahieren und die Methode "authentifizieren" dieser Klasse auszuführen. Das Ergebnis der Ausführung ist ein "Tupel" mit zwei Elementen, das erste der Tupel ist "self.user" und das zweite ist "self.auth".

rest_framework/request.py


    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        """
        for authenticator in self.authenticators:
            try:
                user_auth_tuple = authenticator.authenticate(self)
            except exceptions.APIException:
                self._not_authenticated()
                raise

            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

Schauen wir uns vor diesem Hintergrund die zuvor definierte Authentifizierungsklasse für JWT an.

api/utils/auth.py


class JWTAuthentication(BaseAuthentication):
    keyword = 'JWT'
    model = None

    def authenticate(self, request):
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != self.keyword.lower().encode():
            return None

        if len(auth) == 1:
            msg = "Autorisierung deaktiviert"
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = "Autorisierung Kein ungültiger Speicherplatz"
            raise exceptions.AuthenticationFailed(msg)

        try:
            jwt_token = auth[1]
            jwt_info = jwt.decode(jwt_token, SECRET_KEY)
            userid = jwt_info.get("userid")
            try:
                user = UserInfo.objects.get(pk=userid)
                user.is_authenticated = True
                return (user, jwt_token)
            except:
                msg = "Benutzer existiert nicht"
                raise exceptions.AuthenticationFailed(msg)
        except jwt.ExpiredSignatureError:
            msg = "Token ist Timeout"
            raise exceptions.AuthenticationFailed(msg)

    def authenticate_header(self, request):
        pass

Die Klasse enthält eine Authentifizierungsmethode. Wenn die Authentifizierung erfolgreich ist, wird ein Tupel mit Benutzerinformationen zurückgegeben. Damit ist die Analyse der loginbezogenen Quellen abgeschlossen.

Vielen Dank, dass Sie bis zum Ende bei uns bleiben. : raise_hand_tone1:

Recommended Posts

Implementieren Sie die JWT-Anmeldefunktion im Django REST-Framework
Implementierung der JWT-Authentifizierungsfunktion in Django REST Framework mit djoser
Implementieren Sie die Follow-Funktion in Django
Melden Sie sich mit dem Django Rest Framework an
Implementieren Sie hierarchische URLs mit drf-verschachtelten Routern im Django REST-Framework
Logisches Löschen in Django, DRF (Django REST Framework)
So implementieren Sie Rails-Helfer-ähnliche Funktionen in Django
Tipps zum Django Rest Framework
Implementierung der Authentifizierungsfunktion in Django REST Framework mit djoser
Listenmethode für verschachtelte Ressourcen im Django REST-Framework
Implementieren Sie die API mit explosiver Geschwindigkeit mithilfe des Django REST Framework
Django REST Framework Stolperstein
Django REST Framework mit Vue.js
Implementierung der Login-Funktion in Django
Implementieren Sie die REST-API schnell in Python
[Django] Verwenden Sie MessagePack mit dem Django REST-Framework
Implementierung der benutzerdefinierten Authentifizierungsfunktion für Benutzermodelle in Django REST Framework mit djoser
Erstellen Sie eine RESTful-API mit dem Django Rest Framework
Verstehen Sie den Komfort des Django Rest Framework
Ein Verwaltungstool, das sofort mit dem REST-Framework ng-admin + Django erstellt werden kann
CRUD GET mit Nuxt & Django REST Framework ②
Verschiedene Hinweise zum Django REST-Framework
CRUD POST mit Nuxt & Django REST Framework
CRUD GET mit Nuxt & Django REST Framework ①
Überlegungen zum Design von Django REST Framework + Clean Architecture
Wie man mit verstümmelten Charakteren in json von Django REST Framework umgeht
Implementieren Sie ein benutzerdefiniertes Benutzermodell in Django
So können Sie die Funktionen des Django Rest Frameworks in einer Datei ausprobieren
CRUD PUT, DELETE mit Nuxt & Django REST Framework
Fehlermeldung anzeigen, wenn die Anmeldung in Django fehlschlägt
Übergeben Sie Login-Benutzerinformationen, um sie in Django anzuzeigen
Django REST Framework Ein wenig nützlich zu wissen.
So erstellen Sie eine Rest-API in Django
Modell in Django
Form in Django
Lernnotizen für die Migrationsfunktion im Django-Framework (2)
Erstellen Sie eine Todo-App mit Django REST Framework + Angular
Weitere neue Benutzerauthentifizierungsmethoden mit Django REST Framework
Lassen Sie uns eine Todo-App mit dem Django REST-Framework erstellen
Erstellen Sie eine API für die Benutzerauthentifizierung mit Django REST Framework
[Django Rest Framework] Passen Sie die Filterfunktion mit Django-Filter an
Lernnotizen für die Migrationsfunktion im Django-Framework (1)
Erstellen einer API, die mit BERT im Django REST-Framework negativ-positive Inferenzergebnisse zurückgibt