JWT-authenticatie in Django

Deze tutorial zal een inleiding geven tot JSON Web Tokens (JWT) en hoe JWT-authenticatie in Django geïmplementeerd kan worden.

Wat is JWT?

JWT is een gecodeerde JSON-reeks die wordt doorgegeven in headers om verzoeken te verifiëren. Het wordt meestal verkregen door JSON-gegevens te hashen met een geheime sleutel. Dit betekent dat de server niet telkens opnieuw naar de database hoeft te zoeken om de gebruiker op te halen die aan een bepaald token is gekoppeld.

Hoe JSON Web Tokens werkt

Wanneer een gebruiker zich met succes aanmeldt met zijn inloggegevens, wordt een JSON Web Token verkregen en opgeslagen in de lokale opslag. Wanneer de gebruiker toegang wil tot een beveiligde URL, wordt het token verzonden in de kop van het verzoek. De server controleert vervolgens of er een geldige JWT in de autorisatiekop staat en als deze wordt gevonden, krijgt de gebruiker toegang.

Een typische content header ziet er als volgt uit:

Autorisatie: drager eyJhbGciOiJIUzI1NiIsI

Hieronder is een diagram van dit proces:

Het concept van authenticatie en autorisatie

Verificatie is het proces van het identificeren van een ingelogde gebruiker, terwijl autorisatie het proces is om vast te stellen of een bepaalde gebruiker recht heeft op toegang tot een webresource.

API-voorbeeld

In deze tutorial gaan we een eenvoudig gebruikersauthenticatiesysteem in Django bouwen met JWT als het authenticatiemechanisme.

Vereisten

  • Django
  • Python

Laten we beginnen.

Maak een map waarin u uw project kunt behouden en ook een virtuele omgeving om de projectafhankelijkheden te installeren.

mkdir myprojects cd myprojects virtual venv 

Activeer de virtuele omgeving:

bron venv / bin / activeren 

Maak een Django-project.

django-admin startproject django_auth 

Installeer DRF en django-rest-framework-jwt met behulp van pip.

pip installeer djangorestframework pip installeer djangorestframework-jwt pip installeer django

Laten we doorgaan en DRF toevoegen aan de lijst met geïnstalleerde apps in de settings.py het dossier.

Configureer de JWT-instellingen

Om JWT te kunnen gebruiken, moeten we django-rest-framework-machtigingen configureren om JSON Web Tokens te accepteren.

In de settings.py bestand, voeg de volgende configuraties toe:

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

Maak een nieuwe app met de naam gebruikers die de gebruikersauthenticatie en het beheer zal afhandelen.

cd django-auth django-admin.py startapp-gebruikers 

Voeg de gebruikersapp toe aan de lijst met geïnstalleerde apps in de settings.py het dossier.

De database instellen

We gaan de PostgreSQL-database gebruiken omdat deze stabieler en robuuster is.

Maak het auth database en wijs een gebruiker toe.

Schakel over naar het Postgres-account op uw computer door te typen:

sudo su postgres

Open de Postgres-prompt en maak de database aan:

psql postgres = # CREATE DATABASE auth;

Maak een rol:

postgres = # CREATE ROLE django_auth MET AANMELDEN PASSWORD 'asdfgh'; 

Verleen toegang tot de database voor de gebruiker:

postgres = # TOESTAAN ALLE VOORRECHTEN OP DATABASE auth TO django_auth;

Installeer het psycopg2-pakket, waarmee we de door ons geconfigureerde database kunnen gebruiken:

pip psycopg2 installeren

Bewerk de momenteel geconfigureerde SQLite-database en gebruik de Postgres-database.

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

Modellen maken

Django wordt geleverd met een ingebouwd authenticatiesysteem dat zeer uitgebreid is, maar soms moeten we aanpassingen maken en daarom moeten we een aangepast gebruikersauthenticatiesysteem maken. Ons gebruikersmodel zal erven van de AbstractBaseUser klas aangeboden door django.contrib.auth.models.

In gebruikers / models.py beginnen we met het maken van het gebruikersmodel om de gebruikersgegevens op te slaan.

# users / models.py from __future__ import unicode_literals from django.db importeer modellen uit django.utils import tijdzone van django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin) class User (AbstractBaseUser, PermissionsMixin): "" "Een abstracte basis klasse die een volledig gebruikersmodel implementeert met beheerderslicenties. "" "email = models.EmailField (max_length = 40, unique = True) first_name = models.CharField (max_length = 30, blank = True) last_name = models.CharField ( max_length = 30, blank = 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) retourneer zelf 

VERPLICHTE VELDEN bevat alle vereiste velden op uw gebruikersmodel, behalve het veld voor de gebruikersnaam en het wachtwoord, aangezien deze velden altijd worden gevraagd.

UserManager is de klasse die de definieert create_user en createsuperuser methoden. Deze klasse zou vóór moeten komen AbstractBaseUser klasse die we hierboven hebben gedefinieerd. Laten we doorgaan en het definiëren.

van django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin, BaseUserManager) class UserManager (BaseUserManager): def _create_user (self, email, password, ** extra_fields): "" "Creëert en bewaart een gebruiker met de gegeven e-mail, en wachtwoord. "" "if not email: raise ValueError ('De opgegeven e-mail moet zijn ingesteld') try: with transaction.atomic (): user = self.model (email = email, ** extra_fields) user.set_password (wachtwoord) user.save (using = self._db) return user except: raise def create_user (self, email, password = None, ** extra_fields): extra_fields.setdefault ('is_staff', False) extra_fields.setdefault ('is_superuser', False ) return self._create_user (email, wachtwoord, ** extra_velden) def create_superuser (zelf, email, wachtwoord, ** extra_velden): extra_fields.setdefault ('is_staff', True) extra_fields.setdefault ('is_superuser', True) retourneer zelf ._create_user (email, wachtwoord = wachtwoord, ** extra_velden)

migraties

Migraties bieden een manier om uw databaseschema bij te werken telkens wanneer uw modellen wijzigen, zonder gegevens te verliezen.

Maak een eerste migratie voor ons gebruikersmodel en synchroniseer de database voor de eerste keer.

python manage.py maak migraties gebruikers python manage.py migreren

Een superuser maken

Maak een superuser door de volgende opdracht uit te voeren:

python manage.py maaktuperuser

Nieuwe gebruikers maken

Laten we een eindpunt maken om registratie van nieuwe gebruikers mogelijk te maken. We beginnen met het serialiseren van de velden met gebruikersmodellen. Serializers bieden een manier om gegevens te wijzigen in een vorm die gemakkelijker te begrijpen is, zoals JSON of XML. Deserialisatie doet het tegenovergestelde, namelijk het converteren van gegevens naar een formulier dat kan worden opgeslagen in de database.

Maak gebruikers / serializers.py en voeg de volgende code toe.

# users / serializers.py van rest_framework importeren serializers from.models importeren Gebruikersklasse UserSerializer (serializers.ModelSerializer): date_joined = serializers.ReadOnlyField () class Meta (object): model = Gebruikersvelden = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = 'password': 'write_only': True

CreateUserAPIView

Vervolgens willen we een weergave maken zodat de client een URL heeft voor het maken van nieuwe gebruikers.

Voeg in users.views.py het volgende toe:

# users / views.py class CreateUserAPIView (APIView): # Sta elke gebruiker toe (geverifieerd of niet) om toegang te krijgen tot deze URL permission_classes = (AllowAny,) def post (self, request): user = request.data serializer = UserSerializer (data = gebruiker) serializer.is_valid (raise_exception = True) serializer.save () return Response (serializer.data, status = status.HTTP_201_CREATED)

We gaan zitten permission_classes naar (AllowAny,) om elke gebruiker (geverifieerd of niet) toegang te geven tot deze URL.

URL's configureren

Maak een bestand gebruikers / urls.py en voeg de URL toe die overeenkomt met de weergave die we hebben gemaakt. Voeg ook de volgende code toe.

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

We moeten ook URL's van de gebruikersapp naar het hoofd importeren django_auth / urls.py het dossier. Dus ga je gang en doe dat. We gebruiken de omvatten functie hier, dus vergeet niet om het te importeren.

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

Nu we klaar zijn met het maken van het eindpunt, laten we een test doen en kijken of we op schema liggen. We zullen Postman gebruiken om de tests uit te voeren. Als u niet bekend bent met Postman, is het een hulpmiddel dat een vriendelijke GUI presenteert voor het samenstellen van verzoeken en het lezen van antwoorden.

Zoals je hierboven kunt zien werkt het eindpunt zoals verwacht.

Gebruikers authenticeren

We zullen gebruik maken van de Django-REST Framework JWT Python-module die we aan het begin van deze tutorial hebben geïnstalleerd. Het voegt JWT-authenticatie-ondersteuning toe voor Django Rest Framework-apps.

Maar laten we eerst enkele configuratieparameters definiëren voor onze tokens en hoe ze worden gegenereerd in het settings.py-bestand.

# settings.py import datetime JWT_AUTH = 'JWT_VERIFY': True, 'JWT_VERIFY_EXPIRATION': True, 'JWT_EXPIRATION_DELTA': datetime.timedelta (seconds = 3000), 'JWT_AUTH_HEADER_PREFIX': 'Bearer',
  • JWT_VERIFY: Het zal een jwt.DecodeError ophalen als het geheim verkeerd is.
  • JWT_VERIFY_EXPIRATION: Stelt de vervaltijd in op True, wat betekent dat tokens na verloop van tijd vervallen. De standaardtijd is vijf minuten.
  • JWT_AUTH_HEADER_PREFIX: Het voorvoegsel Autorisatieheaderwaarde dat moet worden verzonden samen met het token. We hebben het zo geplaatst Toonder, en de standaard is JWT.

In gebruikers / views.py, voeg de volgende code toe.

@api_view (['POST']) @permission_classes ([AllowAny,]) def authenticate_user (request): try: email = request.data ['email'] wachtwoord = request.data ['wachtwoord'] user = User.objects .get (email = email, password = password) if user: try: payload = jwt_payload_handler (gebruiker) 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 (afzender = gebruiker .__ class__, request = request, user = user) return Response (user_details, status = status.HTTP_200_OK) except Exception als e: raise e else: res = 'error': 'kan niet authenticeren met de opgegeven inloggegevens of de account is gedeactiveerd' return Response (res, status = status.HTTP_403_FORBIDDEN) behalve KeyError: res = 'error' : 'Geef een e-mail en een wachtwoord op' return Response (res)

In de bovenstaande code worden in de aanmeldingsweergave gebruikersnaam en wachtwoord als invoer gebruikt en wordt vervolgens een token gemaakt met de gebruikersinformatie die overeenkomt met de doorgegeven inloggegevens als payload en wordt deze teruggestuurd naar de browser. Andere gebruikersdetails zoals de naam worden ook samen met het token teruggestuurd naar de browser. Dit token wordt gebruikt voor verificatie bij toekomstige verzoeken.

De machtigingsklassen zijn ingesteld op allowAny aangezien iedereen toegang heeft tot dit eindpunt.

We slaan ook de laatste inlogtijd van de gebruiker op met deze code.

user_logged_in.send (afzender = gebruiker .__ class__, request = request, user = user)

Telkens wanneer de gebruiker een API-aanvraag wil doen, moet deze het token in Auth Headers verzenden om het verzoek te verifiëren.

Laten we dit eindpunt testen met Postman. Open Postman en gebruik de aanvraag om te verifiëren met een van de gebruikers die u eerder hebt gemaakt. Als de inlogpoging succesvol is, ziet het antwoord er als volgt uit:

Gebruikers ophalen en bijwerken

Tot nu toe kunnen gebruikers zichzelf registreren en verifiëren. Ze hebben echter ook een manier nodig om hun informatie op te halen en bij te werken. Laten we dit implementeren.

In users.views.py, voeg de volgende code toe.

class UserRetrieveUpdateAPIView (RetrieveUpdateAPIView): # Laat alleen geverifieerde gebruikers toegang tot deze URL hebben permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get (self, request, * args, ** kwargs): # serializer om het 'User' object in iets dat # kan worden goedgekeurd en naar de klant wordt verzonden. serializer = self.serializer_class (request.user) return Response (serializer.data, status = status.HTTP_200_OK) def put (zelf, verzoek, * args, ** kwargs): serializer_data = request.data.get ('gebruiker', ) serializer = UserSerializer (request.user, data = serializer_data, partial = True) serializer.is_valid (raise_exception = True) serializer.save () return Response (serializer.data, status = status.HTTP_200_OK)

We definiëren eerst de toestemmingsklassen en stellen deze in op IsAuthenticated omdat dit een beveiligde URL is en alleen geverifieerde gebruikers er toegang toe hebben. 

We definiëren vervolgens een krijgen methode om gebruikersgegevens op te halen. Nadat gebruikersgegevens zijn opgehaald, werkt een geverifieerde gebruiker hun gegevens naar wens bij.

Werk uw URL's bij om het eindpunt als volgt te definiëren.

gebruikers / urls.py van .views importeren CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [url (r '^ update / $', UserRetrieveUpdateAPIView.as_view ()),]

Om het verzoek succesvol te laten zijn, moeten de koppen het JWT-token bevatten, zoals hieronder wordt getoond.

Als u probeert een resource aan te vragen zonder de authenticatieheader, krijgt u de volgende foutmelding.

Als een gebruiker langer blijft dan de tijd die is opgegeven in JWT_EXPIRATION_DELTA zonder een verzoek in te dienen, vervalt het token en moeten ze een ander token aanvragen. Dit wordt hieronder ook aangetoond.

Conclusie

Deze tutorial heeft betrekking op wat nodig is om met succes een solide back-end authenticatiesysteem met JSON Web Tokens te bouwen.