Пользователи и аутентификация Django

Опубликовал: Saturday, February 11, 2024 в категории Django | Пока нет комментариев

Механизм сеансов обеспечивает возможность сохранения данных между отдельными запросами. Но надо еще научиться использовать сеансы для аутентификации пользователей. Разумеется, мы не можем слепо верить тому, что пользователь говорит о себе, поэтому необходимо как-то проверить подлинность этой информации.

Естественно, в Django есть средства решения этой типичной задачи (как и многих других). Система аутентификации Django управляет учетными записями пользователей, группами, разрешениями и основанными на cookie пользовательскими сеансами. Эту систему часто называют auth/auth (аутентификация и авторизация). Название говорит о том, что процедура допуска пользователя в систему состоит из двух этапов. Мы должны:

1.     Убедиться, что пользователь является именно тем, за кого себя выдает (<аутентификация). Обычно это делается путем сравнения введенных имени и пароля с хранящимися в базе данных.

2.     Убедиться, что пользователю разрешено выполнять некую операцию (авторизация). Обычно это делается путем поиска в таблице разрешений.

В полном соответствии с этими принципами система аутентификации и авторизации в Django состоит из следующих частей.

•           Пользователи: люди, зарегистрировавшиеся на сайте.

•       Разрешения: двухпозиционные (да/нет) флаги, показывающие, разрешено ли пользователю выполнять некоторую операцию.

•       Группы: общий механизм назначения опознавательной метки и разрешений сразу нескольким пользователям.

•       Сообщения: простой механизм организации очереди и вывода системных сообщений пользователям.

Если вы работали с административным интерфейсом (см. главу 6), то со многими из этих компонентов вы уже знакомы: редактируя пользователей и группы в административном интерфейсе, вы на самом деле изменяете данные в таблицах базы данных, относящихся к системе аутентификации.

Включение поддержки аутентификации

Как и средства поддержки сеансов, система аутентификации реализована в виде приложения Django в django.contrib, которое нужно установить. По умолчанию оно уже установлено, но, если в какой-то момент вы удалили его, следует выполнить следующие действия:

1.     Убедиться, что подсистема сеансов установлена, как описано выше в этой главе. Для отслеживания пользователей, очевидно, нужны cookie, поэтому без подсистемы сеансов не обойтись.

2.     Включите строку ‘django.contrib.auth’ в параметр INSTALLED_APPS и выполните команду manage, ру syncdb, которая добавит в базу данных необходимые таблицы.

3.     Убедитесь, что в параметре MIDDLEWARE_CLASSES присутствует строка ‘django.contrib.auth.middleware.AuthenticationMiddleware’ - после SessionMiddleware.

Завершив установку, можно приступать к работе с пользователями в функциях представлений. Основным средством доступа к данным о пользователе в представлении является объект request.user, который описывает текущего аутентифицированного пользователя. Если пользователь не аутентифицирован, этот атрибут будет ссылаться на объект AnonymousUser (подробности см. ниже).

Узнать, аутентифицирован ли пользователь, позволяет метод is_authen- ticated():

if request.user.is_authenticated(): tt Пользователь аутентифицирован. else:

tt Анонимный пользователь.

Работа с объектом User

Получив объект User - обычно из атрибута request.user или другим способом (см. ниже), - вы получаете возможность обращаться к любым его полям и методам. Объект AnonymousUser реализует только часть этого интерфейса, поэтому всегда следует вызывать метод user.is_ authenticatedQ, а не предполагать, что вы имеете дело с настоящим объектом User. В табл. 14.3 и 14.4 перечислены поля и методы объектов User.

Таблица 14.3. Поля объектов User

Поле

Описание

username

Обязательное. He более 30 символов. Допустимы только буквы, цифры и знаки подчеркивания.

first_name

Необязательное. Не более 30 символов.

last_name

Необязательное. Не более 30 символов.

email

Необязательное. Адрес электронной почты.

password

Обязательное. Свертка пароля (пароли в открытом виде в Django не хранятся). Дополнительные сведения см. в разделе «Пароли».

is_staff

Булевское. Показывает, разрешено ли пользователю работать с административным интерфейсом.

is_active

Булевское. Показывает, можно ли входить в систему от имени данной учетной записи. Вместо того чтобы удалять учетную запись, можно просто присвоить этому полю значение False.

is_superuser

Булевское. Означает, что этому пользователю неявно предоставлены все разрешения.

last_login

Дата и время последнего входа в систему. По умолчанию в момент входа сюда записываются текущие дата и время.

date_joined

Дата и время создания учетной записи. По умолчанию в момент создания сюда записываются текущие дата и время.

Таблица 14.4. Методы объектов User

Метод

Описание

 

is_authenticated()

Для «настоящих» объектов User возвращает True. Это означает, что пользователь аутентифицирован, но ничего не говорит о его разрешениях. Активность пользователя также не проверяется. Возврат True означает лишь, что пользователь успешно аутентифицирован, и ничего более.

 

is_anonymous()

Возвращает True для объектов Anonymousllser (и False для «настоящих» объектов User). Лучше использовать метод is_authenticated().

 

get_full_name()

Возвращает значения полей first_name и last_ name, разделенные пробелом.

 

set_password(passwd)

Устанавливает пароль пользователя, попутно вычисляя его свертку. Объект User при этом не сохраняется в базе данных.

 

Метод

Описание

check_password(passwd)

Возвращает True, если заданная строка совпадает с паролем пользователя. При сравнении учитывается, что пароль хранится в свернутом виде.

get_group_permissions()

Возвращает список разрешений, предоставленных пользователю посредством групп, в которые он входит.

get_all_permissions()

Возвращает список разрешений, предоставленных как самому пользователю, так и группам, в которые он входит.

has_perm(perm)

Возвращает True, если у пользователя есть указанное разрешение perm, представленное в формате " package. codename". Для неактивных пользователей этот метод всегда возвращает

False.

has_perms(perm_list)

Возвращает True, если у пользователя есть все указанные разрешения. Для неактивных пользователей этот метод всегда возвращает False.

has_module_perms(app_label)

Возвращает True, если у пользователя есть хоть какое-нибудь разрешение на доступ к приложению, заданному строкой app_label. Для неактивных пользователей этот метод всегда возвращает False.

get_and_delete_messages()

Возвращает список объектов Message в очереди пользователя и удаляет сообщения из очереди.

email_user(subj, msg)

Отправляет пользователю сообщение по электронной почте. Сообщение отправляется от имени пользователя, указанного в параметре DEFAULT_FROM_EMAIL. Может также принимать третий аргумент f rom_email, который переопределяет адрес отправителя.

Наконец, в объектах User имеются два поля, описывающих отношения типа многие-ко-многим: groups и permissions. Доступ к объектам, связанным с User, производится точно так же, как для любого другого поля отношения типа многие-ко-многим:

tt Определить группы, в которые входит пользователь: myuser.groups = group_list

tt Добавить пользователя в несколько групп: myuser.groups.add(groupl, group2,…)

tt Удалить пользователя из нескольких групп myuser.groups.remove(group1, group2,…)

tt Удалить пользователя из всех групп myuser.groups.clear()

# С разрешениями все точно так же

myuser.permissions = permission_list

myuser.permissions.add(permission1, permission2, …)

myuser.permissions.remove(permission1, permission2, …)

myuser.permissions.clear()

Вход в систему и выход из нее

В Django имеется ряд встроенных функций для обработки входа и выхода пользователя (и некоторых других интересных ситуаций), но прежде чем перейти к ним, посмотрим, как эти задачи решаются вручную. Для этой цели Django предлагает две функции, находящиеся в модуле

django.contrib.auth: authenticateQ и loginQ.

Для аутентификации по заданным имени и паролю следует использовать функцию authenticateQ. Она принимает два именованных аргумента, username и password, и возвращает объект User, если пароль соответствует имени. В противном случае authenticateQB03BpanjaeT None.

»> from django. contrib import auth

»> user = auth.authenticate(username=’john’, password^’secret’) »> if user is not None:

print "Правильно!" … else:

print "Неверный пароль."

Функция authenticateQ только проверяет учетные данные пользователя. Для допуска пользователя к системе служит функция loginQ. Она принимает объекты HttpRequest и User и с помощью подсистемы сеансов сохраняет идентификатор пользователя в сеансе.

В примере ниже показано, как функции authenticateQn loginQ применяются в представлении:

from django.contrib import auth

def login_view(request):

username = request.POST.get(1username’, ”)

password = request.POST.get(1 password’, ”)

user = auth.authenticate(username=username, password=password)

if user is not None and user.is_active:

tt Пароль правилен и пользователь "активный" auth.login(request, user) tt Переадресовать на страницу успешного входа, return HttpResponseRedirect("/account/loggedin/") else:

tt Переадресовать на страницу ошибок

return HttpResponseRedirect("/account/invalid/")

Чтобы завершить сеанс работы с системой, следует вызвать в представлении функцию django.contrib.auth.logoutQ. Она принимает объект HttpRequest и ничего не возвращает:

from django.contrib import auth

def logout_view(request): auth.logout(request)

#       Переадресовать на страницу успешного выхода, return HttpResponseRedirect(‘7account/loggedout/")

Отметим, что auth.logoutQ не возбуждает исключения, если указанный пользователь не был аутентифицирован.

На практике писать собственные функции login и logout нет необходимости; в подсистеме аутентификации уже имеются готовые представления для обработки входа и выхода. Чтобы воспользоваться ими, нужно прежде всего добавить соответствующие образцы в конфигурацию URL:

from django.contrib.auth.views import login, logout

urlpatterns = patternsC’,

#  прочие образцы…

(r’"accounts/login/S1 , login), (r’"accounts/logout/T , logout),

)

По умолчанию Django ассоциирует эти представления с адресами URL

/accounts/login/ и /accounts/logout/.

По умолчанию представление login использует шаблон registration/ login.html (имя можно изменить, передав представлению дополнительный аргумент "template_na[TieM). Форма в шаблоне должна содержать поля username и password. Ниже приводится пример простого шаблона:

{% extends "base.html" %}

{% block content %}

{% if form.errors %}

<p class="errorn>HeBepHoe имя или пароль</р> {% endif %}

<form action^"" method="post">

clabel for="username">MMfl пользователя:</label>

<input type="text" name="username" value="" id="username">

clabel for=,,password,,>Пapoль:</label>

cinput type="password" name="password" value="" id="password"> cinput type="submit" value="login" />

<input type="hidden" name="next" value="{{ next|escape }}" /> </form>

{% endblock %}

В случае успешной аутентификации пользователь по умолчанию будет переадресован на страницу /accounts/profile/. Чтобы изменить URL- адрес, укажите его в атрибуте value скрытого поля next в форме. Это значение можно также передать в параметре GET-запроса к представлению login, тогда оно будет автоматически добавлено в контекст в виде переменной next, которую можно поместить в скрытое поле.

Представление logout работает несколько иначе. По умолчанию оно использует шаблон registration/logged_out. html (обычно он содержит сообщение «Вы успешно вышли из системы»). Однако его можно вызывать с дополнительным аргументом next_page, в котором указывается URL страницы, куда следует перейти.

Разрешение доступа

только аутентифицированным пользователям

Но для чего все это нужно? Конечно, для того чтобы ограничить доступ к некоторым частям сайта.

Простой и прямолинейный способ закрыть доступ к странице состоит в том, чтобы проверить результат, возвращаемый методом request.user. is_authenticated(), и переадресовать пользователя на страницу входа:

from django.http import HttpResponseRedirect

def my_view(request):

if not request.user.is_authenticated():

return HttpResponseRedirectC /accounts/login/?next=%s’ % request.path) # …

или вывести сообщение об ошибке:

def my_view(request):

if not request.user.is_authenticated():

return render_to_response(‘myapp/login_error.html’) # . ..

Можно также воспользоваться вспомогательным декоратором login_ required:

from django.contrib.auth.decorators import login_required

@login_required

def my_view(request): # . ..

Декоратор login_required выполняет следующие действия:

• Если пользователь не аутентифицирован, производится переадресация на URL /accounts/login/, а текущий URL передается в параметре next строки запроса, например: /accounts/login/?next=/polls/3/;

• Если пользователь аутентифицирован, выполнение функции представления продолжается как обычно. Внутри функции представления можно считать, что пользователь аутентифицирован.

Ограничение доступа по результатам проверки

Ограничение доступа в зависимости от наличия определенного разрешения или на основе результатов еще какой-нибудь проверки, с возможной переадресацией на страницу входа, реализуется точно так же.

Самый простой способ состоит в том, чтобы выполнить проверку объекта request.user непосредственно в функции представления. Например, в следующем представлении проверяется, был ли аутентифицирован пользователь и обладает ли он разрешением polls.can_vote (подробнее о механизме работы разрешений см. ниже):

def vote(request):

if request.user.is_authenticated() \

and request.user.has_perm(‘polls.can_vote’)): tt реализация голосования else:

return HttpResponse("Вы не можете принять участие в этом опросе.")

Для таких случаев Django предлагает вспомогательную функцию user_ passes_test. Она принимает аргументы и порождает специализированный декоратор, адаптированный к конкретной ситуации:

def user_can_vote(user):

return user.is_authenticated() and user.has_perm("polls.can_vote")

@user_passes_test(user_can_vote, login_url="/login/") def vote(request):

tt Далее можно предполагать, что пользователь аутентифицирован tt и имеет надлежащее разрешение.

Декоратор user_passes_test принимает один обязательный аргумент: вызываемый объект, который получает на входе объект User и возвращает True, если пользователю разрешен доступ к странице. Отметим, что user_passes_test автоматически не проверяет, был ли пользователь User аутентифицирован; это вы должны сделать самостоятельно.

В примере выше показан также второй (необязательный аргумент) login_url, который позволяет указать URL страницы входа (по умолчанию /accounts/login/). Если пользователь не прошел проверку, декоратор user_passes_test переадресует его на страницу, определяемую аргументом login_url.

Поскольку довольно часто приходится проверять, обладает ли пользователь некоторым разрешением, Django предоставляет для этого случая вспомогательную функцию: декоратор permission_required(). С его помощью предыдущий пример можно переписать так:

from django.contrib.auth.decorators import permission_required

@permission_required(‘polls.can_vote’, login_url=,,/login/") def vote(request): tt …

Отметим, что permission_required() также принимает необязательный аргумент login_url, со значением по умолчанию ‘/accounts/login/’.

Ограничение доступа к обобщенным представлениям

В списках рассылки Django часто задается вопрос - как ограничить доступ к обобщенному представлению? Для этого необходимо написать тонкую обертку вокруг представления, а в конфигурации URL указать ее, а не само обобщенное представление:

from django.contrib.auth.decorators import login_required from django.views.generic.date_based import object_detail

@login_required

def limited_object_detail(*args, **kwargs): return object_detail(*args, **kwargs)

Разумеется, вместо login_required можно использовать любой ограничивающий декоратор.

Управление пользователями, разрешениями и группами

Самый простой способ управления системой аутентификации дает административный интерфейс. В главе 6 рассказывается, как с его помощью редактировать учетные записи пользователей и управлять их разрешениями. В большинстве случаев так вы и будете поступать.

Но существует и низкоуровневый API, которым можно воспользоваться, когда необходим полный контроль. Ниже мы его обсудим.

Создание пользователей

Для создания учетной записи пользователя служит функция create_ user:

»> from django. contrib. auth. models import User »> user = User.objects.create_user(username=’john’,

email=’[email protected]’, password^’glass onion’)

В настоящий момент user - это экземпляр класса User, готовый к сохранению в базе данных (create_user() самостоятельно не вызывает метод

save()). Но перед его сохранением вы можете изменить какие-нибудь атрибуты:

»> user. is_staff = True »> user. save()

Изменение пароля

Для изменения пароля служит функция set_password():

»> user = User, objects. get(username=’ john’) »> user.set_password(‘goo goo goo joob’) »> user.saveO

He изменяйте значение атрибута password напрямую. Пароль хранится в виде свертки, поэтому его нельзя редактировать непосредственно.

Если говорить точнее, атрибут password объекта User - это строка в формате

hashtype$salt$hash

Она состоит из трех частей: типа свертки (hashtype), затравки (salt) и самой свертки (hash), разделенных знаком доллара. Часть hashtype может принимать значение, вычисленное по алгоритму shal (по умолчанию) или md5; это алгоритм несимметричного хеширования пароля. Часть salt - случайная строка, используемая для внесения элемента случайности в свертку пароля. Например:

Sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4

Для вычисления и проверки этих значений реализация вызывает методы User.set_password() и User.check_password().

Свертки с затравкой1

Сверткой (hash) называется несимметричная криптографическая функция. Ее легко вычислить, но практически невозможно обратить, то есть получить из свертки исходное значение.

Если бы пароли хранились в открытом виде, то всякий, получивший доступ к базе паролей, смог бы немедленно узнать пароли всех пользователей. Но коль скоро хранятся не сами пароли, а их свертки, то последствия компрометации базы данных не настолько опасны.

Термин «salted hash» в литературе по криптографии переводится и как «свертка с затравкой», и как «хеш привязка». - Прим.ред.

Однако злоумышленник, завладевший базой паролей, все же может применить атаку полным перебором, то есть вычислить свертку миллионов паролей и сравнивать ее с тем, что хранится в базе. На это потребуется время, но не так много, как вам кажется.

Хуже того, существуют так называемые радужные таблицы (rainbow tables), то есть базы данных, содержащие свертки миллионов паролей, вычисленные заранее. При наличии радужной таблицы опытный злоумышленник может взломать большинство паролей за несколько секунд.

Добавление затравки - по существу, случайного начального значения - к сохраненной свертке увеличивает сложность взлома паролей. Поскольку в каждом пароле используется своя затравка, то применение радужных таблиц становится бессмысленным, и злоумышленник вынужден будет обратиться к атаке полным перебором, которая сама по себе усложняется из-за наличия дополнительной энтропии, вносимой затравкой.

Хотя свертки с затравкой нельзя назвать абсолютно защищенным способом хранения паролей, все же это приемлемый компромисс между безопасностью и удобством.

Управление регистрацией

Вышеперечисленные низкоуровневые средства можно использовать для реализации представлений, позволяющих создавать новые учетные записи. Разные разработчики реализуют регистрацию по-разному, поэтому Django оставляет написание представления на ваше усмотрение. Впрочем, это несложно.

В простейшем варианте представление может запрашивать обязательную информацию о пользователе и создавать учетную запись. Для этой цели в Django имеется встроенная форма:

from django import forms

from django.contrib.auth.forms import UserCreationForm from django.http import HttpResponseRedirect from django.shortcuts import render_to_response

def register(request):

if request.method == ‘POST’:

form = UserCreationForm(request.POST) if form.is_valid():

new_user = form.saveO

return HttpResponseRedirect("/books/")

else:

form = UserCreationForm() return render_to_response("registration/register.html", {

‘form’: form,

))

В этой форме предполагается наличие шаблона с именем registration/ register.html. Выглядеть он может так:

{% extends "base.html" %}

{% block title %}Создать учетную запись{% endblock %>

{% block content %> <1Т1>Создать учетную запись</1Т1>

<form action^"" method="post"> {{ form.as_p >}

<input type=,,submit" уа1ие="Создать учетную запись"> </form> {% endblock %}

Использование данных аутентификации в шаблонах

Объект текущего аутентифицированного пользователя и его разрешения попадают в контекст шаблона, если вы пользуетесь классом

RequestContext (см. главу 9).

Примечание —————————————————————————-

Строго говоря, эти переменные попадают в контекст шаблона, только если используется класс RequestContext и параметр TEMPLATE_CONTEXT_PROCESSORS содержит строку "django.core.context_processors.auth" (по умолчанию так и есть). Подробнее см. главу 9.

При использовании объекта RequestContext информация о текущем пользователе (экземпляр класса User или AnonymousUser) сохраняется в шаблонной переменной {{ user}}:

{% if user.is_authenticated %}

<р>Добро пожаловать, {{ user.username }}. Спасибо, что зашли.</р> {% else %>

<р>Добро пожаловать, незнакомец. Пожалуйста, назовите себя.</р> {% endif %}

Разрешения пользователя хранятся в шаблонной переменной {{ perms }}. Это адаптированный к шаблонам прокси-объект для доступа к нескольким методам проверки разрешений, которые будут описаны ниже.

Объект perms может использоваться двумя способами. Можно написать инструкцию вида {% if perms.polls %}, в которой проверить наличие у пользователя хоть какого-нибудь разрешения для работы с данным приложением, или инструкцию вида {% if perms.polls.can_vote %}, где проверить наличие конкретного разрешения.

Таким образом, разрешения можно проверять внутри шаблона, в инструкциях {% if %}:

{% if perms.polls %> <р>Вам разрешено что-то делать в опросах.</р> {% if perms.polls.can_vote %}

<р>Вы можете голосовать!</р> {% endif %} {% else %>

<р>Вам ничего не разрешено делать в опросах.</р> {% endif %}

Источник: Головатый А., Каплан-Мосс Дж. Django. Подробное руководство, 2-е издание. - Пер. с англ. - СПб.: Символ- Плюс, 2010. - 560 е., ил.

Похожие посты:

Комментировать

Your email address will not be published. Required fields are marked *