JWT-Authentifizierung in Django

Dieses Tutorial enthält eine Einführung in JSON Web Tokens (JWT) und die Implementierung der JWT-Authentifizierung in Django.

Was ist JWT??

JWT ist eine codierte JSON-Zeichenfolge, die in Headern zur Authentifizierung von Anforderungen übergeben wird. Es wird normalerweise durch Hashing von JSON-Daten mit einem geheimen Schlüssel erhalten. Dies bedeutet, dass der Server die Datenbank nicht jedes Mal abfragen muss, um den mit einem bestimmten Token verknüpften Benutzer abzurufen.

Funktionsweise von JSON-Web-Tokens

Wenn sich ein Benutzer erfolgreich mit seinen Anmeldeinformationen anmeldet, wird ein JSON-Web-Token abgerufen und im lokalen Speicher gespeichert. Wenn der Benutzer auf eine geschützte URL zugreifen möchte, wird das Token im Header der Anforderung gesendet. Der Server sucht dann im Authorization-Header nach einer gültigen JWT. Wenn er gefunden wird, erhält der Benutzer Zugriff.

Ein typischer Inhaltsheader sieht so aus:

Genehmigung: Inhaber eyJhbGciOiJIUzI1NiIsI

Unten ist ein Diagramm, das diesen Prozess zeigt:

Das Konzept der Authentifizierung und Autorisierung

Authentifizierung ist der Prozess der Identifizierung eines angemeldeten Benutzers. Die Autorisierung ist der Prozess der Identifizierung, ob ein bestimmter Benutzer das Recht hat, auf eine Webressource zuzugreifen.

API-Beispiel

In diesem Tutorial bauen wir ein einfaches Benutzerauthentifizierungssystem in Django auf, das JWT als Authentifizierungsmechanismus verwendet.

Bedarf

  • Django
  • Python

Lass uns anfangen.

Erstellen Sie ein Verzeichnis, in dem Sie Ihr Projekt aufbewahren, und erstellen Sie eine virtuelle Umgebung, um die Projektabhängigkeiten zu installieren.

mkdir myprojects cd myprojects virtual venv 

Aktivieren Sie die virtuelle Umgebung:

source venv / bin / aktivieren 

Erstellen Sie ein Django-Projekt.

django-admin startprojekt django_auth 

Installieren Sie DRF und Django-Rest-Framework-Jwt mit pip.

pip install djangorestframework pip installieren djangorestframework-jwt pip install django

Gehen wir weiter und fügen Sie DRF zur Liste der installierten Apps im hinzu einstellungen.py Datei.

Konfigurieren Sie die JWT-Einstellungen

Um JWT verwenden zu können, müssen Sie die Berechtigungen für das Django-Rest-Framework so konfigurieren, dass JSON-Web-Token akzeptiert werden.

In dem einstellungen.py Datei, fügen Sie die folgenden Konfigurationen hinzu:

REST_FRAMEWORK = 'DEFAULT_AUTHENTICATION_CLASSES': ('rest_framework_jwt.authentication.JSONWebTokenAuthentication',),

Erstellen Sie eine neue App namens Benutzer, die die Benutzerauthentifizierung und -verwaltung übernimmt.

cd django-auth django-admin.py startapp benutzer 

Fügen Sie die Benutzeranwendung der Liste der installierten Apps im hinzu einstellungen.py Datei.

Einrichten der Datenbank

Wir werden die PostgreSQL-Datenbank verwenden, da sie stabiler und robuster ist.

Erstellen Sie die Auth Datenbank und weisen Sie einen Benutzer zu.

Wechseln Sie zum Postgres-Konto auf Ihrem Computer, indem Sie Folgendes eingeben:

Sudo su postgres

Rufen Sie die Postgres-Eingabeaufforderung auf und erstellen Sie die Datenbank:

psql postgres = # CREATE DATABASE auth;

Erstellen Sie eine Rolle:

postgres = # CRELE ROLE django_auth MIT LOGIN PASSWORD 'asdfgh'; 

Gewähren Sie dem Benutzer Datenbankzugriff:

postgres = # ALLE PRIVILEGES IN DER DATENBANK ERHALTEN auth TO django_auth;

Installieren Sie das Paket psycopg2, das die Verwendung der von uns konfigurierten Datenbank ermöglicht:

pip install psycopg2

Bearbeiten Sie die aktuell konfigurierte SQLite-Datenbank und verwenden Sie die Postgres-Datenbank.

DATABASES = 'default': 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'auth', 'USER': 'django_auth', 'PASSWORD': 'asdfgh', 'HOST': 'localhost', 'PORT': ",

Modelle erstellen

Django verfügt über ein integriertes Authentifizierungssystem, das sehr aufwändig ist. Manchmal müssen wir jedoch Anpassungen vornehmen und müssen daher ein benutzerdefiniertes Benutzerauthentifizierungssystem erstellen. Unser Benutzermodell erbt von AbstractBaseUser Klasse bereitgestellt von django.contrib.auth.models.

In users / models.py erstellen wir zunächst das Benutzermodell zum Speichern der Benutzerdetails.

# users / models.py aus __future__ import unicode_literals aus django.db importiert Modelle aus django.utils import timezone aus django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin) Klasse User (AbstractBaseUser, PermissionsMixin): "" Eine abstrakte Basis Klasse, die ein voll ausgestattetes Benutzermodell mit Admin-kompatiblen Berechtigungen implementiert. "" "email = models.EmailField (max_length = 40, unique = True) first_name = models.CharField (max_length = 30, leer = True) last_name = models.CharField ( max_length = 30, leer = True) is_active = models.BooleanField (default = True) is_staff = models.BooleanField (default = False) date_joined = models.DateTimeField (default = timezone.now) objects = UserManager () USERNAME_FIELD = 'email' REQUIRED_FIELDS = ['first_name', 'last_name'] def save (self, * args, ** kwargs): super (User, self) .save (* args, ** kwargs) geben self zurück 

BENÖTIGTE FELDER enthält alle erforderlichen Felder in Ihrem Benutzermodell, mit Ausnahme des Benutzernamensfelds und des Kennworts, da diese Felder immer nachgefragt werden.

Benutzer Manager ist die Klasse, die das definiert create_user und schafft Superuser Methoden. Diese Klasse sollte vor dem kommen AbstractBaseUser Klasse, die wir oben definiert haben. Lasst uns fortfahren und es definieren.

von django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin, BaseUserManager) Klasse UserManager (BaseUserManager): def _create_user (self, email, password, ** extra_fields): "" "Erstellt und speichert einen Benutzer mit der angegebenen E-Mail und password. "" "Wenn nicht E-Mail: Werterhöhung erhöhen ('Die angegebene E-Mail muss gesetzt sein') Versuchen Sie es mit: transaction.atomic (): user = self.model (email = email, ** extra_fields) user.set_password (password) user.save (using = self._db) Benutzer mit Ausnahme von: create def_user (self, email, password = kein, ** extra_fields) zurückgeben: extra_fields.setdefault ('is_staff', False) extra_fields.setdefault ('is_superuser', False ) self._create_user (E-Mail, Kennwort, ** extra_fields) zurückgeben def create_superuser (selbst, E-Mail, Passwort, ** extra_fields): extra_fields.setdefault ('is_staff', True) extra_fields.setdefault ('is_superuser', True) zurück ._create_user (E-Mail, Passwort = Passwort, ** Zusatzfelder)

Migrationen

Durch Migrationen können Sie Ihr Datenbankschema jedes Mal aktualisieren, wenn sich Ihre Modelle ändern, ohne Daten zu verlieren.

Erstellen Sie eine anfängliche Migration für unser Benutzermodell und synchronisieren Sie die Datenbank zum ersten Mal.

python manage.py Migrationsbenutzer python manage.py migrieren

Superuser erstellen

Erstellen Sie einen Superuser, indem Sie den folgenden Befehl ausführen:

python manage.py erstellt einen Superuser

Neue Benutzer anlegen

Erstellen Sie einen Endpunkt, um die Registrierung neuer Benutzer zu ermöglichen. Wir beginnen mit der Serialisierung der Benutzermodellfelder. Serialisierer bieten eine Möglichkeit, Daten in ein verständlicheres Formular wie JSON oder XML zu ändern. Bei der Deserialisierung wird das Gegenteil durchgeführt. Dabei werden Daten in ein Formular konvertiert, das in der Datenbank gespeichert werden kann.

Erstellen Sie users / serializers.py und fügen Sie den folgenden Code hinzu.

# users / serializers.py aus rest_framework importiere serializers from.models import Benutzerklasse UserSerializer (serializers.ModelSerializer): date_joined = serializers.ReadOnlyField () Klasse Meta (Objekt): model = User fields = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = 'password': 'write_only': True

CreateUserAPIView

Als Nächstes möchten wir eine Ansicht erstellen, so dass der Client eine URL zum Erstellen neuer Benutzer hat.

Fügen Sie in users.views.py Folgendes hinzu:

# users / views.py-Klasse CreateUserAPIView (APIView): # Zulassen, dass ein Benutzer (authentifiziert oder nicht) auf diese URL zugreifen kann. permission_classes = (AllowAny,) def post (self, request): user = request.data serializer = UserSerializer (data = user) serializer.is_valid (raise_exception = True) serializer.save () Rückmeldung (serializer.data, status = status.HTTP_201_CREATED)

Legen wir fest Berechtigungsklassen zu (AllowAny,) um jedem Benutzer (authentifiziert oder nicht) den Zugriff auf diese URL zu erlauben.

URLs konfigurieren

Erstellen Sie eine Datei Benutzer / Urls.py und fügen Sie die URL hinzu, die der von uns erstellten Ansicht entspricht. Fügen Sie auch den folgenden Code hinzu.

# users / urls.py aus django.conf.urls importieren url, Muster aus .views importieren CreateUserAPIView urlpatterns = [url (r '^ create / $', CreateUserAPIView.as_view ()),]

Außerdem müssen URLs aus der Benutzeranwendung in die Hauptanwendung importiert werden django_auth / urls.py Datei. Also mach weiter und mach das. Wir benutzen die umfassen Funktion hier, also vergessen Sie nicht, sie zu importieren.

# django_auth / urls.py von django.conf.urls import url, enthalten von django.contrib import admin urlpatterns = [url (r '^ admin /'), admin.site.urls), url (r '^ user /', include ('users.urls', namespace = "Benutzer")),] 

Nachdem wir nun den Endpunkt erstellt haben, testen wir, ob wir auf dem richtigen Weg sind. Wir werden Postman verwenden, um die Tests durchzuführen. Wenn Sie mit Postman nicht vertraut sind, stellt dies ein Werkzeug dar, das eine benutzerfreundliche grafische Benutzeroberfläche zum Erstellen von Anforderungen und zum Lesen von Antworten bietet.

Wie Sie oben sehen können, funktioniert der Endpunkt wie erwartet.

Benutzer authentifizieren

Wir werden das Django-REST Framework JWT Python-Modul verwenden, das wir zu Beginn dieses Tutorials installiert haben. Die JWT-Authentifizierungsunterstützung für Django Rest Framework-Apps wird hinzugefügt.

Zunächst definieren wir jedoch einige Konfigurationsparameter für unsere Token und wie sie in der Datei settings.py generiert werden.

# settings.py import datetime JWT_AUTH = 'JWT_VERIFY': True, 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta (Sekunden = 3000), 'JWT_AUTH_HEADER_PREFIX': ''
  • JWT_VERIFY: Es wird ein jwt.DecodeError ausgelöst, wenn das Geheimnis falsch ist.
  • JWT_VERIFY_EXPIRATION: Setzt den Ablauf auf "True", was bedeutet, dass Token nach einer bestimmten Zeit ablaufen. Die Standardzeit beträgt fünf Minuten.
  • JWT_AUTH_HEADER_PREFIX: Das Präfix des Autorisierungs-Header-Werts, das zusammen mit dem Token gesendet werden muss. Wir haben es als festgelegt Träger, und der Standard ist JWT.

Im Benutzer / Ansichten.py, Fügen Sie den folgenden Code hinzu.

@api_view (['POST']) @permission_classes ([AllowAny,]) def authenticate_user (Anforderung): try: email = request.data ['email'] password = request.data ['password'] user = User.objects .get (email = email, password = password) Wenn Benutzer: tryload: payload = jwt_payload_handler (user) token = jwt.encode (payload, settings.SECRET_KEY) user_details =  user_details ['name'] = "% s% s "% (user.first_name, user.last_name) user_details ['token'] = token user_logged_in.send (sender = user .__ class__, request = request, user = user) gibt die Antwort zurück (user_details, status = status.HTTP_200_OK) außer Ausnahme als e: raise e else: res = 'error': 'kann sich nicht mit den angegebenen Anmeldeinformationen authentifizieren oder das Konto wurde deaktiviert' Rückmeldung (res, status = status.HTTP_403_FORBIDDEN) mit Ausnahme von KeyError: res = 'error' : "Bitte geben Sie eine E-Mail-Adresse und ein Passwort ein" Antwort zurück (res)

Im obigen Code übernimmt die Anmeldeansicht Benutzername und Kennwort als Eingabe. Anschließend erstellt sie ein Token mit den Benutzerinformationen, die den übergebenen Anmeldeinformationen als Nutzdaten entsprechen, und gibt sie an den Browser zurück. Andere Benutzerdetails wie der Name werden zusammen mit dem Token auch an den Browser zurückgegeben. Dieses Token wird zur Authentifizierung in zukünftigen Anforderungen verwendet.

Die Berechtigungsklassen sind auf festgelegt allowAny da jeder auf diesen Endpunkt zugreifen kann.

Mit diesem Code speichern wir auch die letzte Login-Zeit des Benutzers.

user_logged_in.send (sender = user .__ class__, request = request, user = user)

Jedes Mal, wenn der Benutzer eine API-Anforderung stellen möchte, muss er das Token in Auth-Headern senden, um die Anforderung zu authentifizieren.

Testen Sie diesen Endpunkt mit Postman. Öffnen Sie Postman und verwenden Sie die Anforderung, um sich bei einem der zuvor erstellten Benutzer zu authentifizieren. Wenn der Anmeldeversuch erfolgreich ist, sieht die Antwort folgendermaßen aus:

Benutzer abrufen und aktualisieren

Bisher können sich Benutzer registrieren und sich authentifizieren. Sie benötigen jedoch auch eine Möglichkeit, ihre Informationen abzurufen und zu aktualisieren. Lassen Sie uns das umsetzen.

Im users.views.py, Fügen Sie den folgenden Code hinzu.

Klasse UserRetrieveUpdateAPIView (RetrieveUpdateAPIView): # Erlaubt nur authentifizierten Benutzern den Zugriff auf diese URL. permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get (self, request, * args, ** kwargs): # für das Umwandeln unseres 'User'-Objekts etwas, das # JSONifiziert und an den Client gesendet werden kann. serializer = self.serializer_class (request.user) return Antwort (serializer.data, status = status.HTTP_200_OK) def put (self, request, * args, ** kwargs): serializer_data = request.data.get ('user', ) serializer = UserSerializer (request.user, data = serializer_data, teilweise = true) serializer.is_valid (raise_exception = true) serializer.save () gibt eine Antwort zurück (serializer.data, status = status.HTTP_200_OK)

Wir definieren zuerst die Berechtigungsklassen und setzen auf IsAuthenticated da dies eine geschützte URL ist, können nur authentifizierte Benutzer darauf zugreifen. 

Wir definieren dann eine erhalten Methode zum Abrufen von Benutzerdetails. Nach dem Abrufen von Benutzerdetails aktualisiert ein authentifizierter Benutzer dann seine Details wie gewünscht.

Aktualisieren Sie Ihre URLs, um den Endpunkt wie folgt zu definieren.

Benutzer / urls.py aus .views importieren CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [url (r '^ update / $'), UserRetrieveUpdateAPIView.as_view ()),]

Damit die Anforderung erfolgreich ist, sollten die Header das JWT-Token wie unten gezeigt enthalten.

Wenn Sie versuchen, eine Ressource ohne den Authentifizierungsheader anzufordern, wird der folgende Fehler angezeigt.

Wenn ein Benutzer die angegebene Zeit überschreitet JWT_EXPIRATION_DELTA Das Token verfällt, ohne eine Anforderung zu stellen, und es muss ein anderes Token angefordert werden. Dies wird auch unten gezeigt.

Fazit

In diesem Lernprogramm wurde beschrieben, was erforderlich ist, um ein solides Back-End-Authentifizierungssystem mit JSON-Webtokens erfolgreich aufzubauen.