Django: Autentizace a autorizace

Občas potřebujeme nějaký obsah zpřístupnit pouze určité sortě návštěvníků. Abychom návštěvníky odlišili, musí se autentizovat pomocí identifikačních údajů, což může být například uživatelského jméno a heslo. Django má v sobě zabudovanou podporu takovéto autentizace a také máme možnost autorizovat přístup pomocí přístupových práv a skupin.

Seriál: Hrajeme si s Djangem (16 dílů)

  1. Django: Úvod a instalace 14.8.2009
  2. Django: Nastavení projektu a první pokusy 21.8.2009
  3. Django: Databázový model 28.8.2009
  4. Django: Databázový model podruhé 4.9.2009
  5. Django: Administrace 11.9.2009
  6. Django: Prezentace dat 18.9.2009
  7. Django: Prezentace dat podruhé 25.9.2009
  8. Django: Zpracovávání formulářů 2.10.2009
  9. Django: Autentizace a autorizace 9.10.2009
  10. Django: Nahrávání souborů 16.10.2009
  11. Django: Zabudované aplikace 23.10.2009
  12. Django: Rozšiřování možností Djanga 30.10.2009
  13. Django: Internacionalizace 6.11.2009
  14. Django: Nasazování projektu 13.11.2009
  15. Django: Kešování a škálování 20.11.2009
  16. Django: Závěr 27.11.2009

Na začátek si neodpustím terminologickou poznámku: autentizace (někdy psáno též jako autentikace, případně autentifikace) je proces ověření identity uživatele. Pomocí ní tedy zjistíme, že u počítač pravděpodobně sedí Alice a ne třeba Bob, protože je aktuální návštěvník přihlášen pomocí Aliciných uživatelských údajů. Autorizace se oproti tomu stará o kontrolu oprávnění, tedy aby přístup měli jenom ti uživatelé, kterým jsme ho povolili.

Modul django.contrib.auth

Aplikaci zajišťující autentizaci a autorizaci můžeme nalézt v modulu Djanga django.contrib.auth. Hlavní součástí je model User, který představuje přihlášeného uživatele. Nepřihlášení uživatelé jsou reprezentováni odvozeným modelem AnonymousUser. Ať už je uživatel přihlášen nebo ne, můžeme k jeho modelu přistupovat v pohledech pomocí objektu request.user a v šablonách přes proměnnou {{ user }}. Instance modelu uživatele obsahuje několik zajímavých atributů:

  • user.username: uživatelské jméno
  • user.first_name: křestní jméno
  • user.last_name: příjmení
  • user.email: e-mail
  • user.is_staff: příznak, zda má uživatel povolený vstup do administrace
  • user.is_active: příznak, zda je uživatel aktivní

Kromě atributů máme k dispozici několik důležitých metod:

  • user.is_authenticated(): zkontroluje, zda je uživatel přihlášen
  • user.get_full_name(): celé jméno uživatele
  • user.set_password(): nastaví uživatelovo heslo
  • user.check_password(): ověří, jestli se zadané heslo shoduje s heslem uživatele

Toto není vyčerpávající seznam, popis mnoha dalších atributů a metod lze najít v dokumentaci a některé si ještě představíme později.

Protože je uživatel reprezentován modelem, zacházíme s ním podobně jako s ostatními databázovými modely. Můžeme přidávat nové uživatelské účty, odstraňovat je a upravovat uživatelské údaje. Vytváření nového uživatelského účtu nám může usnadnit funkce create_user:

>>> from django.contrib.auth.models import User
>>> user = User.objects.create_user('honza', 'honza@example.com', 'honzovoheslo')
>>> user.first_name = 'Jan'
>>> user.last_name = 'Novák'
>>> user.save()
>>> user
<User: honza>
>>> user.is_staff
False
>>> user.password
'sha1$32f0e$c445ff000ed85dff88f32f874d7021bee2fe1cc4'
>>> user.check_password('honzovoheslo')
True
>>> user.check_password('jinéheslo')
False
Hesla nejsou uložena v databázi přímo, ale ukládá se tam pouze jejich otisk, takže pokud chceme uživateli heslo změnit nebo ověřit, musíme použít metodu set_password nebo check_password.

Přihlašování a odhlašování

Identita návštěvníka se dá ověřit pomocí funkce authenticate, která vrátí objekt uživatele v případě úspěšného ověření nebo prázdný objekt pokud se ověření nezdaří. Poté uživatele můžeme přihlásit pomocí metody login a kdykoliv později odhlásit pomocí metody logout. Místo toho, abychom se pustili do vytváření pohledů pro přihlašování a odhlašování, využijeme generické pohledy, které byly pro tento účel vytvořeny. Otevřeme si tedy soubor urls.py a připíšeme na konec několik řádků kódu:

urlpatterns += patterns('',
    # ...
    (r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'login.html'}),
    (r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'template_name': 'logout.html'}),
)

Cesty /accounts/login/ a /accounts/logout/ jsou výchozí a dají se přenastavit v souboru settings.py pomocí konstant LOGIN_URL a LOGOUT_URL. Klíčem template_name jsem si vybral jiné umístění šablony, ve výchozím nastavení jsou umístěny v podadresáři registration. Šablona pro přihlašování (soubor templates/login.html) může vypadat třeba takto:

{% extends "base.html" %}

{% block content %}
<h1>{% block title %}Přihlášení{% endblock %}</h1>

{% if form.errors %}
  <p>Špatně zadané uživatelské jméno nebo heslo. Zkuste se prosím přihlásit znovu.</p>
{% endif %}

<form action="." method="POST">
<table>
{% include "form.html" %}
<tr>
<td></td>
<td>
<input type="submit" value="Přihlásit se">
<input type="hidden" name="next" value="{{ next }}">
</td>
</tr>
</table>
</form>
{% endblock %}

V případě neúspěšného přihlášení vypíšeme na sedmém řádku chybovou hlášku. Na dvanáctém řádku je využita šablona pro výpis formuláře z minulého dílu. Sedmnáctý řádek obsahuje skrytou hodnotu formuláře next, což je adresa stránky, na kterou budeme po přihlášení přesměrováni. Pokud není hodnota next zadána, standardně se přejde na /accounts/profile/ (dá se přenastavit pomocí konstanty LOGIN_REDIRECT_URL).

Django 9

Odhlašovací šablona (templates/logout.html) bude oproti té přihlašovací jednodušší:

{% extends "base.html" %}

{% block content %}
<h1>{% block title %}Odhlášeno{% endblock %}</h1>

<p>Byl(a) jste odhlášen(a). Nashle příště!</p>
{% endblock %}

Django 9

Uživatelský profil

Na začátku jsme se dozvěděli, že model uživatele obsahuje jeho jméno, příjmení a e-mail. Ale co když chceme k uživatelskému účtu připojit dodatečné informace? Stačí rozšířit jeho model pomocí tzv. profilu. Otevřeme si soubor video_store/models.py a přidáme tento kód:

from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, verbose_name='Uživatel')
    info = models.TextField('Informace', blank=True)

    def __unicode__(self):
        return self.user.username

    class Meta:
        verbose_name = 'profil'
        verbose_name_plural = 'profily'

Založili jsme si nový model, který k modelu uživatele připojí textové pole, kam můžeme poznamenat jakékoliv další údaje. Na čtvrtém řádku je použit atribut typu OneToOneField, což je obdoba unikátního cizího klíče (ForeignKey s parametrem unique=True). Takováto vazba nám zaručí, že jeden uživatel bude mít právě jeden profil (ne více).

Když máme definovaný model, sesynchronizujeme ho s databází (python manage.py syncdb) a vytvoříme pro něj formulář. V tomto případě ho zčásti můžeme vygenerovat. Otevřeme si soubor video_store/forms.py a vložíme do něj následující kód:

from django.contrib.auth.models import User

class ProfileForm(forms.ModelForm):
    info = forms.CharField(label='Informace', widget=forms.Textarea, required=False)
    password1 = forms.CharField(label='Heslo', widget=forms.PasswordInput, required=False)
    password2 = forms.CharField(label='Heslo znovu', widget=forms.PasswordInput, required=False)

    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')

    def clean_password2(self):
        if self.cleaned_data.get('password1') != self.cleaned_data['password2']:
            raise forms.ValidationError('Zadaná hesla se liší.')

        return self.cleaned_data['password2']

Můžeme zde vidět dvě nové věci. První je, že formulář neodvozujeme od třídy forms.Form, ale forms.ModelForm. Tato třída slouží pro generování formulářů z modelů. V podtřídě Meta specifikujeme atributem model, ze kterého modelu se má formulář generovat. Pokud neuvedeme výčet atributů modelu, které se mají použít, pomocí meta atributů fields nebo exclude, jsou pro generování použity všechny atributy. Druhá nová věc je metoda clean_password2. Máme možnost si definovat vlastní validaci určitého formulářového políčka pomocí metody pojmenované clean_políčko. Pokud uživatelský vstup nevyhovuje, vyhodí se výjimka forms.ValidationError a u daného políčka se vypíše uživateli chybová hláška. V tomto případě kontrolujeme, zda jsou zadaná hesla stejná.

Ještě musíme provázat uživatelský model s profilem. Na to má Django vyhrazenou konstantu AUTH_PROFILE_MODULE, která se tradičně nachází v souboru settings.py. Stačí do něj připsat tento řádek:

AUTH_PROFILE_MODULE = 'video_store.Profile'

Jakmile má tato konstanta přiřazenou cestu k našemu modelu, stačí u objektu uživatele zavolat metodu get_profile. Tato metoda vrací profil daného uživatele nebo vyvolává výjimku Profile.DoesNotExist. Vytvoříme si v souboru video_store/views.py pohled, ve kterém využijeme tohoto propojení:

from models import Profile
from forms import ProfileForm
from django.contrib.auth.decorators import login_required

@login_required
def profile(request, saved):
    try:
        profile = request.user.get_profile()
    except Profile.DoesNotExist:
        profile = Profile(user=request.user)

    if request.method == 'POST':
        form = ProfileForm(request.POST)

        if form.is_valid():
            request.user.first_name = form.cleaned_data['first_name']
            request.user.last_name = form.cleaned_data['last_name']
            request.user.email = form.cleaned_data['email']

            if len(form.cleaned_data['password1']) > 0:
                request.user.set_password(form.cleaned_data['password1'])

            request.user.save()

            profile.info = form.cleaned_data['info']
            profile.save()

            return HttpResponseRedirect('/accounts/profile/saved/')
    else:
        form = ProfileForm(initial={'first_name': request.user.first_name,
                                    'last_name': request.user.last_name,
                                    'email': request.user.email,
                                    'info': profile.info,
                                    })

    return render_to_response('profile.html', {'form': form, 'saved': saved}, context_instance=RequestContext(request))

Před samotnou definicí pohledu je podivný kód @login_required, kterému se říká dekorátor. Je to vlastně funkce, která obalí jinou funkci, jenom je zde použit zkrácený zápis, který se používá v Pythonu 2.4 a novějším. Tento dekorátor zamezí přístup všem nepřihlášeným uživatelům a přesměruje je na stránku s přihlášením. V samotném pohledu zavoláme již zmíněnou metodu get_profile (řádek 8) a v případě, že uživatel nemá přiřazený profil, založíme mu nový (řádek 10). Do databáze ukládáme zvlášť objekt uživatele a zvlášť jeho profil. Pokud uživatel vyplnil kolonku s heslem (řádek 20), změníme mu ho. Všimněte si, že zobrazený formulář (řádek 30) má některá políčka již předvyplněná.

K pohledu je potřeba vytvořit odpovídající šablonu. V ní stačí zobrazit formulář a v případě, že došlo k jeho úspěšnému uložení, vypsat potvrzující hlášku. Šablonu uložíme do souboru templates/profile.html:

{% extends "base.html" %}

{% block content %}
<h1>{% block title %}Uživatelský profil{% endblock %}</h1>

{% if saved %}
  <p>Profil byl upraven.</p>
{% endif %}

<form action="." method="POST">
<table>
{% include "form.html" %}
<tr>
<td></td>
<td><input type="submit" value="Upravit"></td>
</tr>
</table>
</form>
{% endblock %}

A nakonec si můžeme v souboru templates/base.html rozšířit část <div id="menu"> o zobrazování informací o aktuálně přihlášeném uživateli:

{% if user.is_authenticated %}
<ul id="user">
<li>Přihlášen: <a href="/accounts/profile/">{{ user }}</a></li>
{% if user.is_staff %}
<li><a href="/admin/">Administrace</a></li>
{% endif %}
<li><a href="/accounts/logout/">Odhlásit</a></li>
</ul>
{% endif %}

Django 9

Související odkazy

V dalším díle se budeme zabývat nahráváním souborů do aplikace.

Autor je dlouhodobým studentem Fakulty informatiky, webový nadšenec a programátor — nejraději programuje v jazycích Haskell a Python.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 8

Přehled komentářů

yed_ model uzivatelskeho profilu
Pavel Dvořák Re: model uzivatelskeho profilu
langpa Re: model uzivatelskeho profilu
Honza Kral Validace password
Honza Kral Re: Validace password
Pavel Dvořák Re: Validace password
kiot Reset hesla
Pavel Dvořák Re: Reset hesla
Zdroj: https://www.zdrojak.cz/?p=3097