Uma das coisas mais interessantes de usar python para o desenvolvimento, é a facilidade que ele nos traz nas tarefas comuns no dia a dia. Um ótimo exemplo é o Django, um framework python que nos auxilia na criação de soluções web, sejam elas REST APIs ou até mesmo um monolito com as views todas no mesmo projeto.
Trazendo o exemplo do Django, e para mostrar como ele pode alavancar a sua produção no dia a dia de desenvolvimento, hoje quero mostrar para você como configurar uma autenticação via token JWT utilizando o framework.
Conhecendo o JWT
Antes de te mostrar o código, é importante que você saiba o que é um token JWT. O JSON Web Token (JWT) é um padrão aberto (RFC 7519) para criação de tokens compactos e seguros, que podem ser utilizados para autenticação e/ou troca de informações entre partes confiáveis. Ele é amplamente utilizado em sistema distribuídos, autenticação de APIs e Single Sign-On (SSO).
O JWT é composto por três partes codificadas em Base64URL, separadas por pontos (.):
- Header (Cabeçalho): Define o tipo do token (typ: “JWT”) e o algoritmo de assinatura (alg: “HS256”, por exemplo)
- Payload (Corpo): Contém as declarações (claims) do token, que podem ser padrão (como sub, iat, exp) ou personalizadas
- Signature (Assinatura): É gerada combinando o Header e o Payload com uma chave secreta (no caso de HMAC) ou uma chave privada (no caso de RSA ou ECDSA)
Um JWT antes da codificação teria essa cara:
{
"alg": "HS256",
"typ": "JWT"
}
.
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1710451200,
"exp": 1710454800
}
E ao ser gerado, seria bem parecido com isso:
eyJjbGllbnRfaWQiOiJZekV6TUdkb01ISm5PSEJpT0cxaWJEaHlOVEE9IiwicmVzcG9uc2Vf
dHlwZSI6ImNvZGUiLCJzY29wZSI6ImludHJvc2NwZWN0X3Rva2VucywgcmV2b2tlX3Rva2VucyIsImlzcyI6ImJqaElSak0xY1hwYWEyMXpkV3RJU25wNmVqbE1iazQ0YlRsTlpqazNkWEU9Iiwic3ViIjoiWXpFek1HZG9NSEpuT0hCaU9HMWliRGh5TlRBPSIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0Ojg0NDMve3RpZH0ve2FpZH0vb2F1dGgyL2F1dGhvcml6ZSIsImp0aSI6IjE1MTYyMzkwMjIiLCJleHAiOiIyMDIxLTA1LTE3VDA3OjA5OjQ4LjAwMCswNTQ1In0
Exemplo prático com DJango
Agora que você já conhece um pouco sobre o JWT, vamos começar o nosso exemplo prático, primeiro vamos iniciar nosso projeto Django. Recomendo que utilize um ambiente virtual python (pyenv) para seguir o tutorial, apenas para isolar as dependências do projeto, mas caso queira seguir apenas com o python da sua máquina não tem problema.
Vamos começar instalando as dependências do Django:
pip install django djangorestframework markdown django-filter
Agora, as dependências para gerar os Tokens JWT:
pip install djangorestframework-simplejwt
Por fim, vamos criar nosso projeto Django:
django-admin startproject exemplotokens
cd exemplotokens
E agora, criar o app que vai servir como base para o sistema:
django-admin startapp geradordetokens
Pronto, seu projeto Django já está pronto. Agora vamos configurar para que ele seja interpretado como uma Api REST e fazer as primeiras configurações do JWT. Dentro do settings.py do projeto, devemos adicionar as seguintes configuranções:
INSTALLED_APPS = [
...
'rest_framework',
'geradordetokens',
'rest_framework.authtoken',
]
A configuração acima adiciona o rest_framework ao nosso projeto, além do nosso app geradordetokens e a biblioteca responsável por fazer a autenticação por token (rest_framework.authtoken)
Agora, devemos configurar o JWT como o meio padrão de autenticação do nosso projeto, para isso, dentro do settings.py adicionamos:
\REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
]
}
E agora, adicionamos algumas configurações de como o nosso token irá ser gerado:
from datetime import timedelta
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=60),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': True,
'ALGORITHM': 'HS256',
}
Essas são as configurações padrões, que define o vencimento do token em 60min e do refresh token em 1 dia. Além de definir o algoritmo padrão como o HS256, mas você pode ver mais configurações na documentação oficial da biblioteca:
https://django-rest-framework-simplejwt.readthedocs.io/en/latest
Configurando as views
Com as settings configuradas, podemos seguir com as configurações dos nossos endpoints de autenticação e cadastro de usuários. Primeiro, vamos criar os serializers responsáveis para cada endpoint. Caso você não saiba, os serializers são os atores responsáveis por interpretar um JSON para o código Python, e também, passar um código Python para JSON. Dentro de geradordetokens, vamos criar um arquivo serializers.py e adicionar o seguinte:
from rest_framework import serializers
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
class LoginSerializer(serializers.Serializer):
email = serializers.EmailField()
password = serializers.CharField(write_only=True)
class UserRegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True)
class Meta:
model = User
fields = ['email', 'password', 'username']
def create(self, validated_data):
user = User.objects.create_user(
email=validated_data['email'],
password=validated_data['password'],
username=validated_data['username']
)
return user
def validate_email(self, value):
user = User.objects.filter(email=value).first()
if user:
raise ValidationError('Este e-mail já está em uso!')
return value
Agora, por questões de boas práticas, vamos criar um novo arquivo services/authentication.py para isolar a nossa lógica de autenticação. Dentro dele vamos adicionar:
from rest_framework.response import Response
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken
class Authenticator():
def authenticate(self, serializer):
if serializer.is_valid():
email = serializer.validated_data['email']
password = serializer.validated_data['password']
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
return Response({"error": "E-mail ou senha inválidos"}, status=400)
if not user.check_password(password):
return Response({"error": "E-mail ou senha inválidos"}, status=400)
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
return Response({
"access_token": access_token,
"refresh_token": str(refresh),
})
return Response({"error": "E-mail ou senha inválidos"}, status=400)
Aqui, estou apenas verificando o e-mail e a senha do nosso usuário, e caso esteja tudo certo, gero o token de acesso e o refresh token.
Agora sim, podemos gerar nossas views para os endpoints da nossa aplicação. Dentro de views.py adicionamos o seguinte:
from rest_framework.views import APIView
from rest_framework import status
from rest_framework.response import Response
from .serializers import LoginSerializer, UserRegisterSerializer
from .services.authentication import Authenticator
class LoginView(APIView):
def post(self, request):
serializer = LoginSerializer(data=request.data)
auth = Authenticator()
return auth.authenticate(serializer)
class RegisterView(APIView):
def post(self, request):
serializer = UserRegisterSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"message": "Usuário cadastrado com sucesso!"}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Nada muito complicado, o LoginView será o responsável pela autenticação dos nossos usuários. Enquanto o RegisterView, ficará responsável pelo cadastro de novos usuários. Agora, para que isso funcione, vamos cadastrar os endpoints, então dentro de geradordetokens vamos criar o arquivo urls.py e adicionar o seguinte código:
from django.urls import path
from .views import LoginView, RegisterView
urlpatterns = [
path('auth/login/', LoginView.as_view(), name='login'),
path('auth/register/', RegisterView.as_view(), name='register'),
]
Por fim, dentro do urls.py global da nossa aplicação, que fica em exemplotokens/ vamos importar essas configurações:
from django.urls import path, include
urlpatterns = [
path('api/v1/', include('geradordetokens.urls'))
]
E pronto, nossa autenticação via tokens JWT está pronta, além do cadastro de novos usuários.
Testando nosso código:
Agora que temos tudo pronto, vamos testar o que acabamos de fazer. Para os testes, estarei utilizando o postman, mas com os exemplos que deixarei abaixo, você pode facilmente executar pelo terminal do seu sistema operacional usando o CURL. O Postman é apenas para uma melhor visualização.
Antes de rodar o projeto, vamos rodar as migrations do Django, para que as tabelas padrões de usuário sejam criadas:
python manage.py migrate
Caso você não tenha configurado outro banco, um arquivo .sqlite3 será criado dentro dos diretórios do seu projeto, ele será o nosso banco de teste. Caso queira usar outro, não tem problema, irá funcionar na mesma forma.
Agora sim, podemos rodar o nosso projeto:
python manage.py runserver
Primeiro, vamos cadastrar um novo usuário no sistema:
curl --location 'http://127.0.0.1:8000/api/v1/auth/register/' \
--header 'Content-Type: application/json' \
--data-raw '{
"username": "Usuario",
"email": "email@email.com",
"password": "12345678"
}'
A resposta deve ser uma mensagem de sucesso. Caso tenha algum erro, volte no tutorial e seguida cada passo novamente. Agora, vamos fazer o login:
curl --location 'http://127.0.0.1:8000/api/v1/auth/login/' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": "email@email.com",
"password": "12345678"
}'
Como resultado, você deve receber o access_token e o refresh_token:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzQxMDEwMjk5LCJpYXQiOjE3NDEwMDY2OTksImp0aSI6ImFjMmYyNzhiMjRiMDQzY2ZhMzc1NmMwZGYzYTU1ZGI1IiwidXNlcl9pZCI6MX0.kAu-Nxuv-KZzr5PMp9sO62iCOjFy3lJYlpdLVyy-Ods",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTc0MTA5MzA5OSwiaWF0IjoxNzQxMDA2Njk5LCJqdGkiOiI4ZDg2NjIyYzc1MTA0ZjdkOTg2YzJkZTBkMmNmMzQwOCIsInVzZXJfaWQiOjF9.ACiG3itxjgTBaIsj-ldNDJR0I2V1YB4OT4y0SUP0pNk"
}
Pronto, já estamos recebendo nossas credenciais de acesso. Para testar se elas estão passando, vamos adicionar um novo endpoint que solicita o token para ser acessado. Então, dentro das views.py do nosso projeto, vamos utilizar o IsAuthenticated do Django, que é responsável por solicitar credenciais de acesso para que uma rota funcione.
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
class ClassParaAutenticar(APIView):
permission_classes = [IsAuthenticated]
def get(self, request):
return Response({"message": "Estou autenticado"})
Por fim, vamos adicionar ele no nosso urls.py
from django.urls import path
from .views import LoginView, RegisterView, ClassParaAutenticar
urlpatterns = [
path('auth/login/', LoginView.as_view(), name='login'),
path('auth/register/', RegisterView.as_view(), name='register'),
path('autenticado/', ClassParaAutenticar.as_view(), name='rota_autenticada')
]
Agora, se tentarmos acessar esse endpoint sem o token, iremos receber uma mensagem falando sobre o uso obrigatório do token:
curl --location 'http://127.0.0.1:8000/api/v1/autenticado/'
Response:
{
"detail": "Authentication credentials were not provided."
}
Passando o token, nosso acesso será liberado:
curl --location 'http://127.0.0.1:8000/api/v1/autenticado/' \
--header 'Authorization: Bearer <seu_token_vem_aqui>'
Response:
{
"message": "Estou autenticado"
}
Conclusão
Nesse tutorial você acaba de ver como é simples a criação de tokens JWT em uma aplicação Django, apesar de longo, esse artigo busca ser o mais explicativo o possível para que você consiga fazer as suas próprias configurações.