Este tutorial dará uma introdução ao JSON Web Tokens (JWT) e como implementar a autenticação JWT no Django.
O que é JWT?
JWT é uma string JSON codificada que é transmitida em cabeçalhos para autenticar solicitações. Geralmente é obtido por hash de dados JSON com uma chave secreta. Isso significa que o servidor não precisa consultar o banco de dados todas as vezes para recuperar o usuário associado a um determinado token.
Como funcionam os tokens da Web JSON
Quando um usuário faz login com sucesso usando suas credenciais, um JSON Web Token é obtido e salvo no armazenamento local. Sempre que o usuário deseja acessar uma URL protegida, o token é enviado no cabeçalho da requisição. O servidor então verifica se há um JWT válido no cabeçalho de autorização e, se encontrado, o acesso do usuário será permitido.
Um cabeçalho de conteúdo típico terá esta aparência:
Authorization:
Bearer eyJhbGciOiJIUzI1NiIsI
Abaixo está um diagrama que mostra esse processo:
O conceito de autenticação e autorização
A autenticação é o processo de identificação de um usuário logado, enquanto a autorização é o processo de identificação se um determinado usuário tem o direito de acessar um recurso da web.
Exemplo de API
Neste tutorial, vamos construir um sistema simples de autenticação de usuário no Django JWT como mecanismo de autenticação.
Requisitos
Vamos começar.
Crie um diretório onde você manterá seu projeto e também um ambiente virtual para instalar as dependências do projeto.
mkdir myprojects cd myprojects virtual venv
Ative o ambiente virtual:
source venv/bin/activate
Crie um projeto Django.
django-admin startproject django_auth
Instale DRF e django-rest-framework-jwt usando pip.
pip install djangorestframework pip install djangorestframework-jwt pip install django
Vamos adicionar o DRF à lista de aplicativos instalados no settings.py
Arquivo.
Defina as configurações do JWT
Para usar um JWT simples, precisamos configurar as permissões django-rest-framework para aceitar JSON Web Tokens.
No settings.py
arquivo, adicione as seguintes configurações:
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ), }
Crie um novo aplicativo chamado users, que lidará com a autenticação e o gerenciamento do usuário.
cd django-auth django-admin.py startapp users
Adicione o aplicativo de usuários à lista de aplicativos instalados no settings.py
Arquivo.
Configurando o banco de dados
Vamos usar o banco de dados PostgreSQL porque é mais estável e robusto.
Crie o auth
banco de dados e atribuir um usuário.
Mude para a conta Postgres em sua máquina digitando:
sudo su postgres
Acesse o prompt do Postgres e crie o banco de dados:
psql postgres=# CREATE DATABASE auth;
Crie uma função:
postgres=# CREATE ROLE django_auth WITH LOGIN PASSWORD 'asdfgh';
Conceda acesso ao banco de dados ao usuário:
postgres=# GRANT ALL PRIVILEGES ON DATABASE auth TO django_auth;
Instale o pacote psycopg2, que nos permitirá usar o banco de dados que configuramos:
pip install psycopg2
Edite o banco de dados SQLite atualmente configurado e use o banco de dados Postgres.
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', 'NAME': 'auth', 'USER': 'django_auth', 'PASSWORD': 'asdfgh', 'HOST': 'localhost', 'PORT': '', } }
Criando modelos
O Django vem com um sistema de autenticação embutido que é muito elaborado, mas às vezes precisamos fazer ajustes e, portanto, precisamos criar um sistema de autenticação de usuário personalizado. Nosso modelo de usuário será herdado do AbstractBaseUser
classe fornecida por django.contrib.auth.models
.
Em users/models.py, começamos criando o modelo User para armazenar os detalhes do usuário.
# users/models.py from __future__ import unicode_literals from django.db import models from django.utils import timezone from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin ) class User(AbstractBaseUser, PermissionsMixin): """ An abstract base class implementing a fully featured User model with admin-compliant permissions. """ 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) return self
REQUIRED_FIELDS
contém todos os campos obrigatórios em seu modelo de usuário, exceto o campo de nome de usuário e senha, pois esses campos sempre serão solicitados.
UserManager
é a classe que define o create_user
e createsuperuser
métodos. Esta classe deve vir antes do AbstractBaseUser
classe que definimos acima. Vamos em frente e defini-lo.
from django.contrib.auth.models import ( AbstractBaseUser, PermissionsMixin, BaseUserManager ) class UserManager(BaseUserManager): def _create_user(self, email, password, **extra_fields): """ Creates and saves a User with the given email,and password. """ if not email: raise ValueError('The given email must be set') try: with transaction.atomic(): user = self.model(email=email, **extra_fields) user.set_password(password) 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, 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)
Migrações
As migrações fornecem uma maneira de atualizar seu esquema de banco de dados sempre que seus modelos mudam, sem perder dados.
Crie uma migração inicial para nosso modelo de usuários e sincronize o banco de dados pela primeira vez.
python manage.py make migrations users python manage.py migrate
Criando um Superusuário
Crie um superusuário executando o seguinte comando:
python manage.py createsuperuser
Criando novos usuários
Vamos criar um endpoint para habilitar o cadastro de novos usuários. Começaremos serializando os campos do modelo do usuário. Os serializadores fornecem uma maneira de alterar os dados para um formato mais fácil de entender, como JSON ou XML. A desserialização faz o oposto, que é converter dados em um formulário que pode ser salvo no banco de dados.
Crie users/serializers.py e adicione o seguinte código.
# users/serializers.py from rest_framework import serializers from.models import User class UserSerializer(serializers.ModelSerializer): date_joined = serializers.ReadOnlyField() class Meta(object): model = User fields = ('id', 'email', 'first_name', 'last_name', 'date_joined', 'password') extra_kwargs = {'password': {'write_only': True}}
CreateUserAPIView
Em seguida, queremos criar uma visualização para que o cliente tenha uma URL para criar novos usuários.
Em users.views.py, adicione o seguinte:
# users/views.py class CreateUserAPIView(APIView): # Allow any user (authenticated or not) to access this url permission_classes = (AllowAny,) def post(self, request): user = request.data serializer = UserSerializer(data=user) serializer.is_valid(raise_exception=True) serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED)
Montamos permission_classes
para (AllowAny,)
para permitir que qualquer usuário (autenticado ou não) acesse esta URL.
Configurando URLs
Criar um arquivo users/urls.py
e adicione o URL para corresponder à exibição que criamos. Adicione também o seguinte código.
# users/urls.py from django.conf.urls import url, patterns from .views import CreateUserAPIView urlpatterns = [ url(r'^create/$', CreateUserAPIView.as_view()), ]
Também precisamos importar URLs do aplicativo de usuários para o principal django_auth/urls.py
Arquivo. Então vá em frente e faça isso. estamos usando o include
função aqui, então não se esqueça de importá-lo.
# django_auth/urls.py from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^user/', include('users.urls', namespace="users")), ]
Agora que terminamos de criar o endpoint, vamos fazer um teste e ver se estamos no caminho certo. Usaremos o Postman para fazer os testes. Se você não estiver familiarizado com o Postman, é uma ferramenta que apresenta uma GUI amigável para construir solicitações e ler respostas.
Como você pode ver acima, o endpoint está funcionando conforme o esperado.
Autenticação de usuários
Faremos uso do módulo Django-REST Framework JWT Python que instalamos no início deste tutorial. Ele adiciona um suporte de autenticação JWT simples para aplicativos Django Rest Framework JWT.
Mas primeiro, vamos definir alguns parâmetros de configuração para nossos tokens e como eles são gerados no arquivo 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
: Ele gerará um jwt.DecodeError se o segredo estiver errado. -
JWT_VERIFY_EXPIRATION
: define a expiração como True, o que significa que os Tokens expirarão após um período de tempo. O tempo padrão é cinco minutos. -
JWT_AUTH_HEADER_PREFIX
: o prefixo do valor do cabeçalho de autorização que deve ser enviado junto com o token. Nós o configuramos comoBearer
e o padrão éJWT
.
Dentro users/views.py
adicione o seguinte código.
@api_view(['POST']) @permission_classes([AllowAny, ]) def authenticate_user(request): try: email = request.data['email'] password = request.data['password'] user = User.objects.get(email=email, password=password) if user: try: 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) return Response(user_details, status=status.HTTP_200_OK) except Exception as e: raise e else: res = { 'error': 'can not authenticate with the given credentials or the account has been deactivated'} return Response(res, status=status.HTTP_403_FORBIDDEN) except KeyError: res = {'error': 'please provide a email and a password'} return Response(res)
No código acima, a visualização de login usa o nome de usuário e a senha como entrada e, em seguida, cria um token com as informações do usuário correspondentes às credenciais passadas como carga útil e o retorna ao navegador. Outros detalhes do usuário, como nome, também são retornados ao navegador junto com o token. Este token será usado para autenticação em solicitações futuras.
As classes de permissão são definidas como allowAny
já que qualquer um pode acessar este endpoint.
Também armazenamos a hora do último login do usuário com este código.
user_logged_in.send(sender=user.__class__, request=request, user=user)
Toda vez que o usuário deseja fazer uma solicitação de API, ele deve enviar o token em Auth Headers para autenticar a solicitação.
Vamos testar esse endpoint com o Postman. Abra o Postman e use a solicitação para autenticar com um dos usuários que você criou anteriormente. Se a tentativa de login for bem-sucedida, a resposta será semelhante a esta:
Recuperando e Atualizando Usuários
Até agora, os usuários podem se registrar e se autenticar. No entanto, eles também precisam de uma maneira de recuperar e atualizar suas informações. Vamos implementar isso.
Dentro users.views.py
adicione o seguinte código.
class UserRetrieveUpdateAPIView(RetrieveUpdateAPIView): # Allow only authenticated users to access this url permission_classes = (IsAuthenticated,) serializer_class = UserSerializer def get(self, request, *args, **kwargs): # serializer to handle turning our `User` object into something that # can be JSONified and sent to the 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('user', {}) 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)
Primeiro definimos as classes de permissão e definimos como IsAuthenticated
já que esta é uma URL protegida e somente usuários autenticados podem acessá-la.
Definimos então um get
método para recuperar detalhes do usuário. Depois de recuperar os detalhes do usuário, um usuário autenticado atualizará seus detalhes conforme desejado.
Atualize suas URLs para definir o terminal da seguinte maneira.
users/urls.py from .views import CreateUserAPIView, UserRetrieveUpdateAPIView urlpatterns = [ url(r'^update/$', UserRetrieveUpdateAPIView.as_view()), ]
Para que a solicitação seja bem-sucedida, os cabeçalhos devem conter o token JWT conforme mostrado abaixo.
Se você tentar solicitar um recurso sem o cabeçalho de autenticação, receberá o seguinte erro.
Se um usuário permanecer além do tempo especificado em JWT_EXPIRATION_DELTA
sem fazer uma solicitação, o token expirará e eles terão que solicitar outro token. Isso também é demonstrado a seguir.
Conclusão
Este tutorial abordou o que é necessário para criar com sucesso um sistema sólido de autenticação de back-end com JSON Web Tokens. Também cobrimos a autenticação Django JWT ou a autorização Django.
Confira mais artigos e tutoriais Python e Django JWT: