Celem niniejszego artykułu jest zaprezentowanie uwierzytelniania użytkownika, z wykorzystaniem wbudowanego w Django mechanizmu sesji. Z artykułu dowiesz się:
- dlaczego warto skorzystać z domyślnej sesji Django w procesie uwierzytelniania
- jak przeprowadzić proces logowania
- jak zabezpieczyć widok logowania, korzystając z weryfikacji tokenu CSRF
Podejście z wykorzystaniem mechanizmu sesji jest jednym z najprostszych sposobów implementacji uwierzytelniania użytkowników. Jest szczególnie polecane w systemach w których frontend komunikuje się z backendem poprzez API (np zapytania AJAX). Istotne jest tylko aby frontend i backend działały na tej samej domenie (wówczas frontend może np.skorzystać z cookies ustawionych przez backend).
Inną, popularną metodą uwierzytelniania w API jest użycie JWT (Json Web Token). Niestety wdrożenie w bezpieczny sposób JWT może być dość trudne, na co wpływa złożoność projektu. Django natywnie nie wspiera też tego rozwiązania, w związku z czym należy posiłkować się zewnętrznymi bibliotekami. Odpowiednią ochronę widoków logowania zapewniają już funkcjonalności dostarczone przez Django, bez potrzeby używania zewnętrznych narzędzi.
Na czym polega uwierzytelnianie oparte o sesji?
Po stronie klienta (przeglądarka użytkownika) przechowywanie jest cookie sesyjne (sessionid). Cookie jest ustawiane, gdy proces logowania użytkownika przebiegnie pomyślnie. Przy każdym kolejnym żądaniu, sprawdzana jest poprawność cookie. Jeżeli weryfikacja wartości ciasteczka przebiegnie prawidłowo, oznacza to pomyślne uwierzytelnienie.
Ustawienie uwierzytelniania w Django Rest Framework
Uwierzytelnianie oparte o sesję, jest jednym z domyślnych sposobów zdefiniowanym w pliku settings.py DRF (zmienna DEFAULTS w module rest_framework.settings.py). Zatem użytkownik nie musi wykonywać dodatkowych zmian w kodzie aby aktywować ten sposób uwierzytelniania. Jednak dobrą praktyką jest dodanie w pliku setting.py ustawienia DEFAULT_AUTHENTICATION_CLASSES
Endpointy i widoki użytkownika w projekcie
Logowanie
- W pliku serializers.py dodaj następujący kod:
Jak widać korzystamy tutaj ze standardowego użytkownika Django (którego możemy zdefiniować np poprzez panel administracyjny Django)
- W pliku views.py definiujemy następujący widok logowania:
Logika logowania jest tutaj realizowana w sposób, jaki robi to Django korzystając ze swoich wbudowanych mechanizmów. Metoda dispatch jest obsłużona tak samo jak w widoku logowanie w django.contrib.auth.views.LoginView. Natomiast autentykacja użytkownika odbywa się poprzez dostarczony przez DRF AuthTokenSerializer. Serializer sprawdza czy dla podanego loginu i hasła istnieje użytkownik i dostarcza jego instancję w zwalidowanych danych, lub zwraca błąd logowania w przypadku gdy dane są niepoprawne. AuthTokenSerializer do autentykacji używa metody authenticate z modułu django.contrib.auth.
Warto podkreślić, że w widoku jest zdefiniowany sposób autoryzacji:
permission_classes = [permissions.AllowAny]
który pozwala każdemu użytkownikowi na dostęp do endpointu logowania.
Kolejnym wartym podkreślenia ustawieniem jest zabezpieczenie widoku logowania dekoratorem csrf_protect. Zalecane jest aby widoki logowanie zawsze miały włączoną weryfikację tokenu CSRF.
W jaki sposób aplikacja frontendowa może pobrać wartość ciasteczka csrftoken, aby następnie przesłać je w nagłówku X-CSRFToken i prawidłowo przejść proces weryfikacji tokenu CSRF? Wystarczy udostępnić w API prosty widok, który ustawi potrzebne cookie:
Widok użytkownika (widok dostępny tylko po uwierzytelnieniu)
- W pliku views.py dodaj następujący kod:
Kod permission_classes = [permissions.IsAuthenticated] odpowiada za autoryzację. Dostęp do tego widoku będą mieli tylko użytkownicy którzy są zalogowani, a sam widok zwróci dane aktualnie zalogowanego użytkownika (odpowiada za to metoda get_object). Domyślną politykę uprawnień w DRF, można skonfigurować tak, aby domyślnie widoki były dostępne tylko zalogowanych użytkowników.Mmożna to zrobić w pliku settings.py (wówczas nie będzie potrzeby bezpośredniego dodawania permission_classes w widoku. Jeżeli w ustawieniach nie zdefiniujemy DEFAULT_PERMISSION_CLASSES to domyślna wartością jest IsAuthenticated – czyli nieograniczony dostęp dla wszystkich).
Dodaj zatem poniższy kod do pliku settings.py, aby nie musieć za każdym razem skonfigurować odpowiednio widoku:
Wylogowanie
- W pliku views.py dodaj następujący kod:
Wylogowanie użytkownika odbywa się poprzez wywołanie funkcji logout z modułu django.contrib.auth.
Na koniec dodaj wszystkie widoku do pliku urls.py:
Testowanie widoków
Na koniec, przy pomocy narzędzia Postman, przetestujmy nasze widoki.
1. Pobieranie csrf token:
W odpowiedzi otrzymamy Cookie o nazwie crsftoken. Wartość tokena będzie potrzebna przy logowaniu.
2. Logowanie
Pamiętaj aby w sekcji Headers ustawić nagłówek X-CSRFToken z wartością csrftoken. Po wprowadzeniu poprawnych danych logowania i wysłaniu zapytania, nastąpi proces uwierzytelniania.
Po poprawnym zalogowaniu zostanie również ustawione ciasteczko sesyjne sessionid.
Ciasteczko to będzie przesyłane w kolejnych requestach i dzięki niemu Django będzie w stanie uwierzytelnić użytkownika.
3. Profil użytkownika.
Profil jest dostępny tylko i wyłącznie dla zalogowanego użytkownika, czyli dla takiego którego cookie sessionid jest ustawione i prawidłowe.
4. Wylogowanie
Wylogowanie usuwa ciastko sessionid.
Podsumowanie
Proces uwierzytelniania poprzez API, przedstawiony w tym artykule jest prostym, szybkim w implementacji oraz przede wszystkim bezpiecznym rozwiązaniem (ponieważ korzystamy tu z mechanizmów autentykacji i autoryzacji napisanych przez twórców Django). W wielu projektach gdzie frontend i backend działają na tej samej domenie, takie rozwiązanie może okazać się najlepsze.