Django – #37 – REST API cz. 12 – Uwierzytelnianie przy pomocy Tokena

Wprowadzenie.

We wpisie przedstawiłem jedną z metod uwierzytelniania, którą dostarcza nam Django Rest Famework. Uwierzytelnianie to jest realizowane przy pomocy tak zwanego Tokena, który może być na przykład generowany w momencie tworzenia konta użytkownika.

Zakres artykułu.

  • Uwierzytelnianie przy pomocy Tokena

Uwierzytelnianie przy pomocy Tokena

W pierwszej kolejności należy dodać rest_framework.authtoken do listy aplikacji INSTALLED_APPS, w pliku z ustawieniami settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'restapiapp',
    'rest_framework',
    'rest_framework.authtoken', # new
]

Ponieważ funkcjonalność ta dodaje do bazy danych tabele, w związku z tym kolejnym krokiem jest z migrowanie bazy danych.

$ python manage.py migrate

Następnie w pliku settings.py możemy, ale nie musimy nadać globalne metody uwierzytelniania dla naszego API. Globalne ustawienia będę automatycznie przypisane do każdego widoku API, chyba że nadamy inne lokalne ustawienia, które nadpiszą ustawienia globalne.

W celu ustawienia globalnych metod uwierzytelniania w pliku settings.py należy dodać następujący fragment kodu.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ]
}

Następnie należy zmodyfikować serializer, który jest używany do tworzenia nowego użytkownika. W tym serializerze musimy dodać w nadpisanej metodzie create linijkę kodu odpowiedzialną za generowanie tokena token = Token.objects.create(user=user).

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'password', 'email')
        extra_kwargs = {
            'password': {
                'write_only': True,
                'style': {'input_type': 'password'}
                }
            }
    
    def create(self, validated_data):
        user = User(email=validated_data['email'], username=validated_data['username'])
        user.set_password(validated_data['password'])
        user.save()
        token = Token.objects.create(user=user) # new
        return user

Obecnie możemy przejść do testowania naszej aplikacji. W przeglądarce wpiszmy adres 127.0.0.1:8000/api/user/create.

Następnie wprowadźmy dane nowego użytkownika i naciśnijmy przycisk POST.

Jeżeli dane wprowadziliśmy poprawnie, wówczas strona zwróci nam nazwę oraz e-mail nowo stworzonego użytkownika.

W następnym kroku zalogujmy się na konto admina. W panelu admina przejdźmy do nowej tabeli Tokens w celu sprawdzenia, czy wygenerował się token dla nowego użytkownika.

Następnie przejdźmy do tabeli Users, gdzie podejrzyjmy czy dane nowe użytkownika poprawnie się zapisały.

Jeżeli wszystko się zgadza, sprawdźmy, czy jesteśmy w stanie zalogować się na naszego nowego użytkownika. W tym celu wylogujmy się z konta admina, wejdźmy na stronę logowania, którą dostarcza nam framework, a następnie wprowadźmy dane.

Przy obecnej konfiguracji powinniśmy otrzymać komunikat “Authentication credentials were not provided”. Oznacza to, że uwierzytelnianie przy pomocy loginu i hasła dla tego widoku nie jest dozwolone, bo jak pamiętamy globalna metoda uwierzytelniania, akceptuje tylko uwierzytelnianie przy pomocy tokena, natomiast w API dla tego widoku nie nadpisaliśmy dodatkowych metod. W tym miejscu jeszcze warto zaznaczyć, że gdy tworzyliśmy nowego użytkownika, to nie otrzymaliśmy takiego komunikatu, ponieważ klasa tego widoku zawierała nadpisanie dozwolone metody uwierzytelniania.

Wprowadźmy teraz kolejną modyfikację, tak abyśmy byli w stanie uzyskać dostęp do danych przy uwierzytelnieniu również poprzez login oraz hasło.

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ]
}

Przetestujmy czy teraz jesteśmy w stanie uzyskać dostęp do danych.

Jak możemy zobaczyć, po zalogowaniu się, otrzymaliśmy poprawnie dane.

Na koniec proponuję jeszcze zmodyfikować atrybuty wprowadzanego adresu e-mail podczas zakładania nowego konta, tak aby adres e-mail był polem unikalnym i nie mógł się powtórzyć.

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'password', 'email')
        extra_kwargs = {
            'password': {
                'write_only': True,
                'style': {'input_type': 'password'}
                }
            }
    
    def create(self, validated_data):
        user = User(email=validated_data['email'], username=validated_data['username'])
        user.set_password(validated_data['password'])
        user.save()
        token = Token.objects.create(user=user)
        return user

    # new
    def validate_email(self, value):
        lower_email = value.lower()
        if User.objects.filter(email__iexact=lower_email).exists():
            raise serializers.ValidationError("This e-mail already exists")
        return lower_email

Przetestujmy teraz, co się stanie, jak zarejestrujemy nowego użytkownika z tym samym adresem e-mail.

Jak widzimy, API zwraca nam błąd i informuje nas, że taki adres e-mail już istnieje.

Autor artykułu
Dominik Bednarski

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.