Django – #17 – System Autoryzacji cz. 5

Wprowadzenie.

W piątej części poświęconej systemowi autoryzacji zajmę się przedstawieniem w jaki sposób wykonać system wylogowywania się przy pomocy wbudowanych mechanizmów Django. Dodatkowo wprowadzę niewielkie modyfikacje paska nawigacyjnego, tak aby przycisk wylogowywania nie był dostępy, gdy użytkownik jeszcze się nie jest zalogowany.

Zakres artykułu.

  • System wylogowywania użytkownika
  • Testy

System wylogowywania użytkownika

W celu wylogowania się z naszej aplikacji należy w widoku odnoszącym się do wylogowywania skorzystać z metody logout, która przyjmuje tylko jeden argument: request. Wylogowywanie należy wykonać po otrzymaniu żądania POST. Taka konstrukcja jest wymagana z tego względu, że przeglądarki często, aby przyspieszyć działanie kolejnych stron wczytują sobie inne strony. W przypadku gdyby wylogowywanie było obsługiwane żądaniem GET, taka przeglądarka mogłaby wykonać procedurę wylogowywania co skutkowałoby, brakiem możliwości zalogowania się do naszej aplikacji (a właściwie zalogowania się i natychmiastowego wylogowania się), bez widocznych objawów, które mogłyby podpowiedzieć, gdzie leży problem z logowaniem.  

Funkcja widoku umożliwiająca wylogowywanie wygląda następująco:

def logout_page(request):
    if request.method == 'POST':
        auth.logout(request)
        return redirect('home')

Natomiast cały plik views.py przedstawia się następująco:

from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib.auth import authenticate
from django.contrib import auth


# Create your views here.


def home(request):
    context = {}
    if request.user.is_authenticated:
        # Do something for authenticated users.
        context['userStatus'] = 'zalogowany'
    else:
        # Do something for anonymous users.
        context['userStatus'] = 'niezalogowany'
    return render(request, 'auth_system/home.html', context)

def signup_page(request):
    context = {}
    if request.method == 'POST':
        # Request for sign up
        # Check if user is available
        try:
            user = User.objects.get(username=request.POST['username'])
            context['error'] = 'Podana nazwa użytkownika już istnieje! Proszę podać inną nazwę użytkownika.'
            return render(request, 'auth_system/signup.html', context)
        except User.DoesNotExist:
            # Check if the password1 is equal to the password2
            if request.POST['password1'] != request.POST['password2']:
                context['error'] = 'Podane hasła nie są takie same! Proszę wprowadzić identyczne hasła.'
                return render(request, 'auth_system/signup.html', context)
            else:
                # Create new user
                user = User.objects.create_user(request.POST['username'], password=request.POST['password1'])
                # Automatic login after signing up
                auth.login(request, user)
                # Go to home page
                return redirect('home')
    else:
        return render(request, 'auth_system/signup.html', context)

def login_page(request):
    context = {}
    if request.method == 'POST':
        user = auth.authenticate(username=request.POST['username'] ,password=request.POST['password'])
        if user is not None:
            auth.login(request, user)
            return redirect('home')
        else:
            context['error'] = 'Podane hasło lub login są błędne! Podaj poprawne dane.'
            return render(request, 'auth_system/login.html', context)
    else:
        return render(request, 'auth_system/login.html')

def logout_page(request):
    if request.method == 'POST':
        auth.logout(request)
        return redirect('home')

Tak jak napisałem na początku zajmiemy się teraz zmodyfikowaniem paska nawigacyjnego. W tym celu skorzystamy z konstrukcji {% if %} {% else %} {% endif %} gdzie warunek będzie stwierdzał, czy jest uwierzytelniony. {% if user.is_authenticated %}. W przypadku jeżeli nie jesteśmy zalogowani do aplikacji, wówczas wyrażenie user.is_authenticated zwróci nam None i do renderowania zostanie wykorzystana tylko cześć kodu po wyrażeniu {% else %}. Więcej o wylogowywaniu znajdziemy pod tym linkiem.

<div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
      <h5 class="my-0 mr-md-auto font-weight-normal">AUTH SYSTEM</h5>
      <nav class="my-2 my-md-0 mr-md-3">
        <a class="btn btn-outline-primary" href="{% url 'home' %}">HOME</a>
        {% if user.is_authenticated %}
        <a class="btn btn-outline-danger" href="JavaScript:{document.getElementById('logout').submit()}">Wyloguj się</a>
        {% else %}
        <a class="btn btn-outline-primary" href="{% url 'signup_page' %}">Zarejestruj się</a>
        <a class="btn btn-outline-primary" href="{% url 'login_page' %}">Zaloguj się</a>
        {% endif %}
      </nav>
      {% if user.is_authenticated %}
      <form id="logout" method="POST" action="{% url 'logout_page' %}">
        {% csrf_token %}
        <input type="hidden">
      </form>
      {% endif %}
    </div>

Cały kod strony prezentuje się następująco:

<!doctype html>
<html lang="en">

  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
    <link rel="shortcut icon" href="#" />
    <title>Hello, world!</title>
  </head>
  <body>
    <div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm">
      <h5 class="my-0 mr-md-auto font-weight-normal">AUTH SYSTEM</h5>
      <nav class="my-2 my-md-0 mr-md-3">
        <a class="btn btn-outline-primary" href="{% url 'home' %}">HOME</a>
        {% if user.is_authenticated %}
        <a class="btn btn-outline-danger" href="JavaScript:{document.getElementById('logout').submit()}">Wyloguj się</a>
        {% else %}
        <a class="btn btn-outline-primary" href="{% url 'signup_page' %}">Zarejestruj się</a>
        <a class="btn btn-outline-primary" href="{% url 'login_page' %}">Zaloguj się</a>
        {% endif %}
      </nav>
      {% if user.is_authenticated %}
      <form id="logout" method="POST" action="{% url 'logout_page' %}">
        {% csrf_token %}
        <input type="hidden">
      </form>
      {% endif %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
    {% block content %}{% endblock %}
    </body>
</html>

W ten oto sposób możemy przejść do przetestowania tego co napisaliśmy.

Testy

Testy rozpoczniemy wpisując w przeglądarce standardowy adres 127.0.0.1:8000. Widok jaki powinniśmy zobaczyć wygląda następująco:

Po naciśnięciu na przycisk Zaloguj się powinno nas przenieść do strony logowania.

Po poprawnym wpisaniu danych powinniśmy zostać przeniesieni na stronę domową, natomiast widok paska nawigacyjnego powinien się zmienić, gdzie przycisk Zaloguj się oraz Zarejestruj się powinny zniknąć a powinien pojawić się przycisk Wyloguj się.  

Po naciśnięciu na przycisk wyloguj się. Ponownie nas powinno przenieść na stronę domową z tym, że widok paska ponownie uległ zmianie, a właściwie wrócił do poprzedniego wyglądu, gdzie nie ma przycisku Wyloguj się, a za to są przyciski Zaloguj się oraz Zarejestruj się

W ten oto sposób stworzyliśmy mechanizm, który umożliwia nam wylogowywanie się z naszej aplikacji.

Autor artykułu
Dominik Bednarski

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *