Autenticazione JWT in Django

Questo tutorial fornirà un'introduzione ai JSON Web Tokens (JWT) e su come implementare l'autenticazione JWT in Django.

Cos'è JWT?

JWT è una stringa JSON codificata che viene passata nelle intestazioni per autenticare le richieste. Di solito è ottenuto da hash dati JSON con una chiave segreta. Ciò significa che il server non ha bisogno di interrogare il database ogni volta per recuperare l'utente associato a un determinato token.

Come funzionano i token Web JSON

Quando un utente accede correttamente utilizzando le proprie credenziali, un token Web JSON viene ottenuto e salvato nella memoria locale. Ogni volta che l'utente desidera accedere a un URL protetto, il token viene inviato nell'intestazione della richiesta. Il server verifica quindi un JWT valido nell'intestazione Autorizzazione e, se trovato, l'utente può accedere.

Un'intestazione di contenuto tipica sarà simile a questa:

Autorizzazione: Bearer eyJhbGciOiJIUzI1NiIsI

Di seguito è riportato un diagramma che mostra questo processo:

Il concetto di autenticazione e autorizzazione

L'autenticazione è il processo di identificazione di un utente che ha effettuato l'accesso, mentre l'autorizzazione è il processo per identificare se un determinato utente ha il diritto di accedere a una risorsa Web.

Esempio di API

In questo tutorial, stiamo costruendo un semplice sistema di autenticazione utente in Django usando JWT come meccanismo di autenticazione.

Requisiti

  • Django
  • Pitone

Iniziamo.

Crea una directory in cui manterrai il tuo progetto e anche un ambiente virtuale per installare le dipendenze del progetto.

mkdir myprojects cd myprojects virtual venv 

Attiva l'ambiente virtuale:

fonte venv / bin / activate 

Crea un progetto Django.

django-admin startproject django_auth 

Installa DRF e django-rest-framework-jwt usando pip.

pip installa djangorestframework pip installa djangorestframework-jwt pip installa django

Andiamo avanti e aggiungere DRF all'elenco delle app installate in settings.py file.

Configurare le impostazioni JWT

Per utilizzare JWT, è necessario configurare le autorizzazioni di django-rest-framework per accettare i token Web JSON.

Nel settings.py file, aggiungere le seguenti configurazioni:

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

Crea una nuova app chiamata utenti che gestirà l'autenticazione e la gestione degli utenti.

cd django-auth django-admin.py utenti startapp 

Aggiungi l'applicazione utente all'elenco delle app installate in settings.py file.

Impostazione del database

Useremo il database PostgreSQL perché è più stabile e robusto.

Crea il auth database e assegnare un utente.

Passa all'account Postgres sul tuo computer digitando:

sudo su postgres

Accedere al prompt di Postgres e creare il database:

psql postgres = # CREATE DATABASE auth;

Crea un ruolo:

postgres = # CREATE ROLE django_auth WITH LOGIN PASSWORD 'asdfgh'; 

Concedere l'accesso al database all'utente:

postgres = # CONCEDERE TUTTI I PRIVILEGI SU DATABASE auth TO django_auth;

Installa il pacchetto psycopg2, che ci permetterà di utilizzare il database che abbiamo configurato:

pip installa psycopg2

Modifica il database SQLite attualmente configurato e utilizza il database Postgres.

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

Creazione di modelli

Django è dotato di un sistema di autenticazione incorporato che è molto elaborato, ma a volte è necessario apportare delle modifiche e quindi è necessario creare un sistema di autenticazione utente personalizzato. Il nostro modello utente erediterà dal AbstractBaseUser classe fornita da django.contrib.auth.models.

In users / models.py, iniziamo creando il modello User per memorizzare i dettagli dell'utente.

# users / models.py da __future__ import unicode_literals dai modelli di importazione django.db dal fuso orario di importazione di django.utils da django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin) classe User (AbstractBaseUser, PermissionsMixin): "" "Una base astratta classe che implementa un modello utente con funzionalità complete con autorizzazioni conformi all'amministratore. "" "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 (Utente, self) .save (* args, ** kwargs) return self 

CAMPI RICHIESTI contiene tutti i campi obbligatori sul modello dell'utente, ad eccezione del campo del nome utente e della password, poiché questi campi verranno sempre richiesti.

UserManager è la classe che definisce il creare un utente e createsuperuser metodi. Questa classe dovrebbe venire prima del AbstractBaseUser classe che abbiamo definito sopra. Andiamo avanti e definiamolo.

da django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin, BaseUserManager) class UserManager (BaseUserManager): def _create_user (self, email, password, ** extra_fields): "" "Crea e salva un utente con l'e-mail specificata, e password. "" "se non e-mail: genera ValueError ('L'e-mail fornita deve essere impostata') prova: con transaction.atomic (): user = self.model (email = email, ** extra_fields) user.set_password (password) user.save (using = self._db) return user tranne: 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, password, ** extra_fields) def create_superuser (self, email, password, ** extra_fields): extra_fields.setdefault ('is_staff', True) extra_fields.setdefault ('is_superuser', True) return self ._create_user (email, password = password, ** extra_fields)

migrazioni

Le migrazioni forniscono un modo per aggiornare lo schema del database ogni volta che i modelli cambiano, senza perdere i dati.

Crea una migrazione iniziale per il modello degli utenti e sincronizza il database per la prima volta.

python manage.py effettua la migrazione degli utenti python manage.py migrare

Creazione di un superutente

Creare un superutente eseguendo il seguente comando:

python manage.py createsuperuser

Creazione di nuovi utenti

Creiamo un endpoint per abilitare la registrazione di nuovi utenti. Inizieremo serializzando i campi del modello Utente. I serializzatori forniscono un modo per modificare i dati in un modulo che è più facile da capire, come JSON o XML. La deserializzazione fa il contrario, ovvero la conversione di dati in un modulo che può essere salvato nel database.

Crea utenti / serializers.py e aggiungi il seguente codice.

# users / serializers.py from rest_framework import serializers from.models import User class UserSerializer (serializers.ModelSerializer): date_joined = serializers.ReadOnlyField () classe Meta (oggetto): model = Campi utente = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = 'password': 'write_only': True

CreateUserAPIView

Successivamente, vogliamo creare una vista in modo che il client abbia un URL per la creazione di nuovi utenti.

In users.views.py, aggiungi quanto segue:

# users / views.py class CreateUserAPIView (APIView): # Consenti a qualsiasi utente (autenticato o meno) di accedere a questo URL permission_classes = (AllowAny,) def post (self, request): user = request.data serializer = UserSerializer (data = utente) serializer.is_valid (raise_exception = True) serializer.save () return Response (serializer.data, status = status.HTTP_201_CREATED)

Prepariamo permission_classes a (AllowAny,) per consentire a qualsiasi utente (autenticato o meno) di accedere a questo URL.

Configurazione degli URL

Crea un file utenti / urls.py e aggiungi l'URL per abbinare la vista che abbiamo creato. Aggiungi anche il seguente codice.

# users / urls.py dall'URL di importazione django.conf.urls, modelli da .views import CreateUserAPIView urlpatterns = [url (r '^ create / $', CreateUserAPIView.as_view ()),]

Dobbiamo inoltre importare gli URL dall'applicazione utente al principale django_auth / urls.py file. Quindi vai avanti e fallo. Stiamo usando il includere funzione qui, quindi non dimenticare di importarlo.

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

Ora che abbiamo finito di creare l'endpoint, facciamo un test e vediamo se siamo sulla buona strada. Useremo Postman per fare i test. Se non hai familiarità con Postman, è uno strumento che presenta una GUI amichevole per la costruzione di richieste e la lettura di risposte.

Come puoi vedere sopra, l'endpoint funziona come previsto.

Autenticazione degli utenti

Faremo uso del modulo Python JWT di Django-REST Framework che abbiamo installato all'inizio di questo tutorial. Aggiunge il supporto dell'autenticazione JWT per le app Django Rest Framework.

Ma prima, definiamo alcuni parametri di configurazione per i nostri token e come sono generati nel file settings.py.

# 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: Solleverà un jwt.DecodeError se il segreto è sbagliato.
  • JWT_VERIFY_EXPIRATION: Imposta la scadenza su True, il che significa che i token scadranno dopo un certo periodo di tempo. Il tempo predefinito è cinque minuti.
  • JWT_AUTH_HEADER_PREFIX: Il prefisso del valore dell'intestazione Autorizzazione richiesto per essere inviato insieme al token. Lo abbiamo impostato come Portatore, e il valore predefinito è JWT.

Nel utenti / views.py, aggiungi il seguente codice.

@api_view (['POST']) @permission_classes ([AllowAny,]) def authenticate_user (request): try: email = request.data ['email'] password = request.data ['password'] utente = User.objects .get (email = email, password = password) se utente: try: payload = jwt_payload_handler (utente) 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 (mittente = utente .__ class__, request = richiesta, utente = utente) return Response (user_details, status = status.HTTP_200_OK) tranne Exception come e: raise e else: res = 'error': 'non può autenticare con le credenziali fornite o l'account è stato disattivato' return Response (res, status = status.HTTP_403_FORBIDDEN) tranne KeyError: res = 'error' : "fornire una e-mail e una password" return Response (res)

Nel codice sopra, la vista di login accetta nome utente e password come input, quindi crea un token con le informazioni dell'utente corrispondenti alle credenziali passate come payload e le restituisce al browser. Altri dettagli utente come il nome vengono anche restituiti al browser insieme al token. Questo token verrà utilizzato per l'autenticazione nelle richieste future.

Le classi di autorizzazione sono impostate su allowAny dal momento che chiunque può accedere a questo endpoint.

Inoltre memorizziamo l'ultimo tempo di accesso dell'utente con questo codice.

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

Ogni volta che l'utente vuole fare una richiesta API, deve inviare il token in Authers Headers per autenticare la richiesta.

Proviamo questo endpoint con Postman. Apri Postman e utilizza la richiesta per l'autenticazione con uno degli utenti creati in precedenza. Se il tentativo di accesso ha esito positivo, la risposta sarà simile a questa:

Recupero e aggiornamento degli utenti

Finora, gli utenti possono registrarsi e autenticarsi. Tuttavia, hanno anche bisogno di un modo per recuperare e aggiornare le loro informazioni. Implementiamo questo.

Nel users.views.py, aggiungi il seguente codice.

class UserRetrieveUpdateAPIView (RetrieveUpdateAPIView): # Consenti solo agli utenti autenticati di accedere a questo url permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get (self, request, * args, ** kwargs): # serializzatore per gestire la trasformazione del nostro oggetto 'User' in qualcosa che # può essere JSONified e inviato al client. serializer = self.serializer_class (request.user) return Response (serializer.data, status = status.HTTP_200_OK) def put (self, request, * args, ** kwargs): serializer_data = request.data.get ('utente', ) 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)

Prima definiamo le classi di autorizzazione e impostiamo su IsAuthenticated poiché questo è un URL protetto e solo gli utenti autenticati possono accedervi. 

Quindi definiamo a ottenere metodo per recuperare i dettagli dell'utente. Dopo aver recuperato i dettagli dell'utente, un utente autenticato aggiornerà i propri dettagli come desiderato.

Aggiorna i tuoi URL per definire l'endpoint come segue.

users / urls.py da .views import CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [url (r '^ update / $', UserRetrieveUpdateAPIView.as_view ()),]

Affinché la richiesta abbia esito positivo, le intestazioni devono contenere il token JWT come mostrato di seguito.

Se si tenta di richiedere una risorsa senza l'intestazione di autenticazione, si otterrà il seguente errore.

Se un utente rimane oltre il tempo specificato in JWT_EXPIRATION_DELTA senza effettuare una richiesta, il token scadrà e dovranno richiedere un altro token. Questo è anche dimostrato di seguito.

Conclusione

Questo tutorial ha coperto ciò che è necessario per creare con successo un solido sistema di autenticazione back-end con Token Web JSON.