Auth
Obsah stránky (hide)
1. Prokousávání se
Mám již funkční aplikaci a chci jí rozšířit o autentifikaci. Použiji dekorátor login_required:
from django.contrib.auth.decorators import login_required @login_required def nejake_view(request):
Nyní při přístupu k danému view je vyžadováno, aby byl přihlášený uživatel. Pochopitelně není, tak se objeví chyba, že nemůže najít url accounts/login/ - tj. nejí-li uživatel přihlášen, je přesměrován na toto url a my si ho musíme přidat do našich url (adresu lze změnit v settings.LOGIN_URL - tahle je výchozí).
#urls.py import django.contrib.auth.views ... url(r'^accounts/login/$', 'django.contrib.auth.views.login'),
Takže jsme se dostali do vestavěného view "login" a posouváme se o jednu chybu dále: Nemáme šablonu 'registration/login.html'. Tu umístíme buď to templates pro projekt, nebo do templates pro aplikaci.
Příklad šablony najdeme u popisu funkce "login" dole na této stránce. Obshuje položky pro username a password a skryté pole pro položku "next". Odesílá se (action=) sobě - tj login. Při úspěšném přihlášení (děje se samo) se provede dekorované view. Při neúspěchu uvidíme opět stránku s přihlašovacím formulářem a chybovou hláškou.
No a to je kupodivu všechno! :-O :-) (pro začátek).
2. Autentifikace
Django má vestavěný systém pro autentifikaci uživatelů, včetně formulářů atd. Výchozí nastavení už obsahuje v souboru settings.py vše potřebné.
django.contrib.auth
Vestavěný systém je minimalistický. Existují nástroje třetích stran s bohatší výbavou.
Základním vestavěným prvkem je model User
from django.contrib.auth.models import User
2.1 atributy třídy User
povinné:
- username
- password
nepovinné:
- first_name (30zn.)
- last_name (30zn.)
- groups - ManyToMany(Groups)
- user_permissions - ManyToMany(Permissions)
- is_staff (bool)
- is_active (bool) - je účet platný? (heslo o.k., ale zneplatněn)
- is_superuser (bool)
- last_login (autom.)
- date_joined (autom.)
Heslo nejde nastavit napřímo (tedy jde ale nebude fungovat). Používat metodu set_password(). Hesla jsou ukládána šifrovaně.
2.2 metody instance
- get_username()
- is_anonymous()
- is_authenticated()
- get_full_name()
- set_password(raw_password)
- check_password(raw_password)
- set_unusable_password()
- has_usable_password()
- get_group_permissions(obj=None)
- get_all_permissions(obj=None)
- has_perm(perm, obj=None)
- has_perms(perm_list, obj=None)
- has_module_perms(package_name)
- email_user(subject, message, from_email=None)
2.3 metody manageru (třídy)
models.UserManager
- create_user(username, email=None, password=None, **extra_fields)
- create_superuser(self, username, email, password, **extra_fields)
User.objects.create_user('jenda', 'jenda@email.com', 'jen-heslo')
Extra_fields jsou nepovinné položky modelu User.
models.AnonymousUser
Tato třída je jako normální User, ale přednastaví :
- id na None
- is_staff, is_superuser = False
- is_active = False
- groups, user_permissions - prázdné
- is_anonymous = True
- is_authenticated = False
- set_password(), check_password(), save() a delete() vyhodí NotImplementedError.
2.4 Autentifikace uživatele
django.contrib.auth import authenticate user = authenticate(username='nekdo', password='heslo')
Vrátí objekt uživatele při úspěšném ověření, jinak None.
from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: # the password verified for the user if user.is_active: print("User is valid, active and authenticated") else: print("The password is valid, but the account has been disabled!") else: # the authentication system was unable to verify the username and password print("The username and password were incorrect.")
2.5 Permissions (oprávnění) a autorizace
Django má vlastní systém oprávnění, který používá ve svém Admin rozhraní, ale je volně k dispozici. Oprávnění je možné udělovat na úrovni uživatele nebo skupiny (uživatele pak do skupin přidáváme).
Ve výchozím stavu se Django postará, aby ke každému modelu byla vytvořena oprávnění pro přidání, změnu a smazání. Tato oprávnění jsou v tabulce auth_permissions. Jsou to názvy oprávnění, přidělit je uživatalům a skupinám se teprve musí. Je to tedy jakýsi "číselník" oprávnění k jednotlivým modelům.
Oprávnění můžeme testovat na instanci modelu User:
user.has_perm('model_add') user.has_perm('model_change') user.has_perm('model_delete')
Vlastní oprávnění
Můžeme definovat na úrovni modelu v části Meta, nebo přímo v programu:
from myapp.models import BlogPost from django.contrib.auth.models import Group, Permission from django.contrib.contenttypes.models import ContentType content_type = ContentType.objects.get_for_model(BlogPost) permission = Permission.objects.create(codename='can_publish', name='Can Publish Posts', content_type=content_type)
2.6 Groups - skupiny
django.contrib.auth.models.Group
Uživatel může patřit do více skupin (M:N). Uživatel automaticky získává všechna oprávnění přidělená skupině v níž se nachází.
2.7 Autentifikace ve Web Requests
Objekty request jsou propojeny s autentifikačním systémem protřednictvím sessions a middleware. Request obsahuje atribut user, který představuje aktuálního uživatele: request.user
Není-li uživatel přihlášen, je request.user nastaven na AnonymousUser. Je-li, pak je to instace třídy User.
if request.user.is_authenticated(): # Do something for authenticated users. else: # Do something for anonymous users.
Přihlášeni a odhlášení uživatele
Autentifikovaného uživatele můžeme vložit do session pomocí funkce login:
from django.contrib.auth import authenticate, login
def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) # Redirect to a success page. else: # Return a 'disabled account' error message else: # Return an 'invalid login' error message.
Před voláním login je vždy nutné volat authenticate! To nastaví u uživatele atribut s informací, který backend ho autentifikoval. Bez toho to nejede :-)
Odhlášení děláme funkcí logout:
from django.contrib.auth import logout def logout_view(request): logout(request) # Redirect to a success page.
Logout nevyhodí žádnou výjimku, pokud uživatel přihlášený nebyl. Logout vymaže všechna data vztahující se k uživateli ze session.
2.8 Omezení přístupu na přihlášené uživatele
Na hrubo:
def my_view(request): if not request.user.is_authenticated(): return redirect('/login/?next=%s' % request.path)
Použití dekorátoru
from django.contrib.auth.decorators import login_required
login_required([redirect_field_name=REDIRECT_FIELD_NAME, login_url=None]) @login_required def my_view(request): ...
Je-li uživatel je přihlášen, pak se view normálně provede.
Není-li uživatel přihlášen, pak dojde k přesměrování na settings.LOGIN_URL (výchozí je 'accounts/login'. Do proměnné "next" dotazu je vložena cesta aktuálního view/url pro možnost vrátit se do view, které uživatele na stránku s přihlášením odeslalo ('/accounts/login?next=/současné/url').
Pokud se nám nelíbí jméno "next", můžeme to změnit prvním parametrem redirect_field_name. Druhý parametr "login_url" je snad jasný.
Další dekorátory:
- user_passes_test(func[, login_url=None]) - zavolá funkci, předá jí User jako parametr a očekává True/False.
- permission_required([login_url=None, raise_exception=False]). Příklad: permission_required('polls.may_vote'). Je-li raise_exception=True, pak místo přesměrování na přihlašovací stránku se zavolá se view pro chybu 403.
Není-li vůbec přihlášený, uplatní se nepovinný parametr login_url s chováním jako u login_required.
Máme-li class based view (class, ne def), pak dekorátor umístíme do definice třídy před metodu dispatch (viz orig. dokumentace class based views. Příklad:
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator from django.views.generic import TemplateView class ProtectedView(TemplateView): template_name = 'secret.html' @method_decorator(login_required) def dispatch(self, *args, **kwargs): return super(ProtectedView, self).dispatch(*args, **kwargs)
2.9 Views pro autentifikaci
Jedná se o vestavěné pomůcky. Nestačí-li, můžeme se dostat o level níže s vestavěnými autentifikačními forms. Všechna vestavěná views vracejí instanci TemplateResponse, což usnadňuje úpravu dat před jejich zobrazením.
- login(request[, template_name, redirect_field_name, authentication_form, current_app, extra_context])
- logout(request[, next_page, template_name, redirect_field_name, current_app, extra_context])
- logout_then_login(request[, login_url, current_app, extra_context])
- password_change(request[, template_name, post_change_redirect, password_change_form, current_app, extra_context])
- password_change_done(request[, template_name, current_app, extra_context])
- password_reset(request[, is_admin_site, template_name, email_template_name, password_reset_form, token_generator, post_reset_redirect, from_email, current_app, extra_context])
- password_reset_done(request[, template_name, current_app, extra_context])
- password_reset_confirm(request[, uidb36, token, template_name, token_generator, set_password_form, post_reset_redirect, current_app, extra_context])
- password_reset_complete(request[, template_name, current_app, extra_context])
login()
login(request[, template_name, redirect_field_name, authentication_form, current_app, extra_context])
jméno URL: login
- template_name - default "registration/login.html".
- redirect_field_name - default "next".
- authentication_form - typicky jméno třídy formuláře, default "AuthenticationForm".
- current_app - ?
- extra_context - slovník s dodatečnými daty
Tyto proměnné předáváme v url dispatcheru. Např:
(r'^accounts/login/$', 'django.contrib.auth.views.login', {'template_name': 'myapp/login.html'}),
Je-li zavolána metodou:
- GET (tedy z dekorátoru), pak zobrazí přihlašovací formulář, který má action nasměrován opět na sebe sama (login).
- POST (tedy z formuláře s přihlašovacími daty), pokusí se uživatele přihlásit. Zadaří-li se, pak přesměruje na url v proměnné "next" (default "accounts/profile", nebo url funkce, kde byl dekorátor). Při neúspěchu o přihlášení je znovu zobrazen přihlašovací formulář.
Šablonu login.html si musíme napsat sami. Šablona obdrží tyto kontextové proměnné:
- form - Form objekt představující AutentificationForm
- next - url kam se přesměrovat po úspěšném přihlášení
- site -
- site_name -
Příklad šablony:
{% extends "base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} <form method="post" action="{% url 'django.contrib.auth.views.login' %}"> {% csrf_token %} <table> <tr> <td>{{ form.username.label_tag }}</td> <td>{{ form.username }}</td> </tr> <tr> <td>{{ form.password.label_tag }}</td> <td>{{ form.password }}</td> </tr> </table> <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next }}" /> </form> {% endblock %}
Chceme-li si to uzpůsobit, koukneme do orig. dokumentace :-)
logout()
3. Můj vlastní příklad a postup
urls.py import django.contrib.auth.views url(r'^accounts/login/$', django.contrib.auth.views.login, name='login'), url(r'^accounts/logout/$', django.contrib.auth.views.logout, name='logout'), settings.py # Máme-li aplikaci v nějakém poadresáři web serveru, # například http//www.aplik.com/podadreář/ # vzniklý jako WSGIScriptAlias /podadresář /home/...: LOGIN_URL = '/podadresář/accounts/login' LOGOUT_URL = '/podadresář/accounts/logout' PROFILE_URL = '/podadresář/accounts/profile' base.html # podmíněné přidání odkazu pro odhlášení {% if user.is_authenticated %} <a href="{% url 'logout' %}">odhlásit se</a> {% endif %} my_app/templates/registration/logged_out.html {% extends 'my_app/base.html' %} {% block title %} odhlášení {% endblock %} {% block obsah %} <p>Odhlášení proběhlo úspěšně. <a href="{% url 'index' %}">Zpět na přihlášení.</a> </p> {% endblock %} my_app/templates/registration/login.html {% extends 'my_app/base.html' %} {% block title %} přihlášení {% endblock %} {% block obsah %} <form method="post" action="{% url 'login' %}"> {% csrf_token %} <table> # v oficiální dokumentaci je následující tabulka rozepsaná, ale tohle je jednodušší.;) # navíc nám to zobrazí í případné chybové hlášky (v dokumentaci si je tam zobrazují ručně). {{ form.as_table }} <tr> <td> </td> <td> <input type="submit" value="přihlásit" /> </td> </tr> </table> <input type="hidden" name="next" value="{{ next }}" /> </form> {% endblock %} my_app/views.py from django.contrib.auth.decorators import login_required # a pro každé view: @login_required def funkce(request): ... *** konec příkladu ***