Как добавить систему аутентификации пользователей для django сайта используя allauth и реакт
31.01.2025
29
0
0
0
0
Введение
Да, давно я не писал про этот сайт. И теперь считаю своим долгом закончить его во чтобы-то ни стало. Итак, нужно разработать и добавить систему аутентификации и регистрации пользователей на сайт. Как это сделать и как это будет выглядеть?
Здесь на самом деле нет ничего сложного. Сам подумай, нам необходимо разработать следующие элементы:
Форму входа
Форму регистрации
Форму изменения пароля
Форму выхода
Форму подтверждения почты + шаблон
Форму сброса пароля + шаблон
Модальное окно для профиля пользователя
И, собственно говоря, всё ... Хотя AllAuth необязательно интегрировать на сайт, можно реализовать регистрацию и управление пользователями самостоятельно, это всё таки будет проще. Нам останется реализовать только фронтенд часть сайта.
Я не очень сильно хочу усложнять и перегружать данную статью имплементацией JWT-токенов или реализации таких архитектур управления учётными профилями пользователей, как SAML2 или LDAP, или даже регистрацию через социальные сети. Это будет в следующих проектах и статьях, ибо она и так получилась слишком большой.
То есть в этой статье я опишу процесс интеграции AllAuth библиотеки на django сайт с минимально необходимым функционалом.
Пишем фронтенд для формы входа и регистрации
Для начала подготовим контейнеры в которых будем рисовать наши модальные окна. Добавь следующие строчки кода в шаблон base.html, сразу перед тегом "main" и после конца блока с шапкой сайта:
В моём случае тег с атрибутом data-type="profile" будет играть роль ещё одной кнопки в шапке сайта. Она будет открывать карточку пользователя. Карточка пользователя будет отрисовываться в виде модального окна, которое будет находиться в теге с id="auth-modal".
Теперь подключим новый компонент Auth. Этот реакт-компонент и будет рисовать для нас модальные окна Входа,Регистрации, карточку пользователя и прочие. В файле компонента src/index.js, сделаем импорт:
Сам файл компонента реализует компонент под названием Auth, который через пропсы определяет какое модальное окно показать и как обратиться к серверу, соответственно. Наверняка, есть более простой и элегантный способ сделать всё то, что я сейчас сделал, но мне просто нужны точки общения с сервером откуда я смогу получать шаблоны для входа и регистрации и менять сайт соответственно. Вот шаблонный файл src/components/Auth.js:
Теперь не много о структуре и работе данного модуля. Есть главный экспортируемый модуль - Auth. Это собственно, модальное окно, которое будет содержать, и карточку пользователя, и форму входа, и формы регистрации, и прочие формы.
Ещё есть два вида функций on* и fetch*. Первые общаются с сервером, вторые обновляют модальное окно. Не обязательно следовать данному принципу, но так легче поддерживать код.
Дальше есть функции updateEmailи updateSession. Они занимаются ровно тем, чем они обзываются, обновляют информацию об адресе почты и обновляют информацию о текущей сессии. Так же добавил такие дополнительные функции как:
getCookie(name) - на случай если захочу получить что-нибудь и куки браузера.
getCSRFToken() - почти каждый запрос требует csrf-токен поэтому написал отдельную функцию
На счёт, глобального объекта URLs. Это не весь список доступных путей к allauth api, но минимально необходимый для нас. Можно конечно и удалить его, и прописывать все URL вручную, но это уже без меня. Двигаемся дальше.
Надо не забыть поменять компонент Header в src/components/header.js. При создании кнопок в шапке сайта я создаю кастомные события, к которым подключу модальное окно, вот так:
Поменяй всё что у тебя было на выделенные строчки
Подключаем Allauth к приложению
Для начала хотел бы отметить официальную документацию, она полная и вполне самодостаточная, хотя, чтобы разобраться в том как использовать данную библиотеку конкретно на своём сайте потребуется некоторое время и изучение официальных примеров. Слава им, они у них есть.
Как всегда, начнём с установки необходимых пакетов. Пакет django-allauth обязателен, но django-allauth[socialaccount] нет, если не нужно использовать соцсети в качестве провайдера для входа. Но я конечно же установлю их оба:
Дальше необходимо добавить следующий allauth-бэкенд в settings.py:
Allauth разработан как встраиваемое django-приложение, вернее целая куча приложений, которые необходимо подключить:
Также будет необходимо подключить и настроить email-backend. С той лишь целью, чтобы протестировать подтверждение отправленной почты и возможность сбросить пароль. Я подключу тестовый бэкенд, ибо это просто быстрее и нагляднее. Все сообщения будут записываться в файлы. В дальнейшем мы конечно же поменяем его на нормальный email-бэкенд.
Добавь эти строчки где-нибудь в settings.py
В файле settings.py мы закончили. Осталось только добавить пути и провести миграцию:
По факту, всё готово, но не совсем. Мне не нужно перенаправляться на отдельную страницу (это не то, как мой django-react-сайт работает). В идеале мне нужен лишь api, а дизайн я уж и сам сделаю. И чтобы получить доступ к api allauth, потребуется ещё кое-что докрутить. Опять же, официальна документация вполне самодостаточна насчёт этого вопроса.
Добавь следующие приложения
И ещё нужно добавить новые пути к api allauth
В файле Website/urls.py
Это делать необязательно, но всё же. Раз уж весь фронтенд на реакте, то и лишние страницы мне тоже не нужны, а значит мой сайт будет использовать allauth в режиме "headless only". Для этого добавь данные строчки, где-нибудь в settings.py:
Как ты мог заметить мы добавили ещё словарь HEADLESS_FRONTEND_URLS. Он будет необходим когда будем реализовывать подтверждение почты или сброса пароля, а пока не беспокойся из-за этого.
Связываем всё вместе
С этого момента мы можем поступить следующим образом; В тупую написать POST запросы к API allauth прямо из реакта (нам нужно буквально 4 таких запроса: на вход /auth/login, на регистрацию /auth/signup, на подтверждение почты /auth/email/verify и на восстановление пароля /auth/password/request). При этом, никакое django-приложение не потребуется.
Кстати, весь headless API для выполнения запросов можно посмотреть тут, !!крайне рекомендую!!.
Но можно поступить по другому; Написать отдельное приложение, собрать там необходимые ModelForm-ы отрендерить их на сервере и при необходимости возвращать клиенту. Это куда дольше, но позволит тебе и мне лучше понять внутреннюю работу allauth пакета и более того, такой подход более гибкий в плане дизайна и отказоустойчивости приложения.
Мы пойдём по первому пути ибо Реакт ... >﹏<
Пишем запросы на сервер (Фронтенд)
Делаем карточку пользователя
Начнём мы с карточки пользователя. Конечно она будет не слишком большой и сложной, скорее даже наоборот. Минимум функционала, только необходимые данные. Пока что без привязки к другим соц. сетям.
Сначала мы получаем статус сессии, то есть узнаём гость это или уже зарегистрированный пользователь. После, если это зарегистрированный пользователь, отображаем всю инфу которая может ему пригодиться (почта, тип почты, имя). И конечно же добавляем кнопки для взаимодействия.
Как-то так, зарегистрированный пользователь
Как-то так, не зарегистрированный пользователь
К каждой кнопке была привязана функция обновления модального окна. И дальше мы будем разбирать, то как это окно меняется и как будет отправляться запрос на сервер.
Форма и запрос на регистрацию
Для регистрации пользователя я использую 4 поля (одно из которых опционально) (+1 одно поле скрыто, это поле мы заполняем csrf-токеном). Так же меняю заголовок и основную кнопку модального окна.
При нажатии кнопки PROCEED, отправляется форма. Ну это если кратко, а если развёрнуто то ... Сначала открывается спинер, после собираем все необходимые данные с формы, проверяем их. Делаем POST-запрос не сервер по пути /_allauth/browser/v1/auth/signup т. е. URLs.SIGNUP, предварительно оставив в заголовке запроса csrf-токен и требуемый формат ответа. Дальше, вне зависимости от ответа, мы закрываем форму и оставляем сообщение пользователю об успехе запроса.
Форма и запрос на вход
Форма входа, проще чем регистрация. Тут требуется только имя пользователя и его пароль.
Мы создаём три корневых элемента и меняем соответственно саму форму, кнопку и заголовок формы.
Отправляем форму по адресу /_allauth/browser/v1/auth/login т.е. URLs.LOGIN.
Форма и запрос на изменение пароля
Для измения пароля нам потребуется отдельная форма. В функции fetchPasswordChange, мы меняем форму, заголовок и кнопку подтверждения. Так же, я запрашиваю ввести новый пароль дважды, ну на всякий случай...
Перед тем как отправить запрос на изменение пароля, нужно проверить совпадают ли новые пароли друг с другом. После чего делаем POST-запрос по адресу /_allauth/browser/v1/account/password/change или URLs.CHANGE_PASSWORD.
Форма и запрос на сброс пароля
Обновляем модальное окно т.е. заголовок, форму и саму кнопку конечно же. Для отправки запроса на востановление пароля потребуется только адресс электронной почты.
Итак как это вообще работает? Пользователь забыл пароль и хочет его востановить. Для этого он отравляет POST-запрос по адресу /_allauth/browser/v1/auth/password/request или URLs.REQUEST_PASSWORD_RESET. Этот запрос отправляется на сервер, откуда будет отправленно электронное письмо на указаный адрес.
Функция запроса на востановление пароля
Письмо отправилось. В письме будет ссылка на страницу с ключом в адресе. Нужно перейти по данному адресу чтобы попасть на такую вот страницу.
Из коробки django-allauth не предоставляет страницу для обработки запростов по востановлению пароля или верификации почты (ЕСЛИ КОНЕЧНО ЭТО НЕ HEADLESS РЕЖИМ). В противном случае будет перенаправление на шаблонную страницу предоставляемую при обычном режиме.
На этой странице нужно заполнить данную форму. То есть ввести новый пароль. Было бы наверное лучше если бы я ещё запрашивал подтверждение пароля, но сойдёт и так. В действительности форма должна быть отправленна с двумя полями:
новый пароль
сгенерированный ключ
В данном случае я не делал разделение на on* и fetch* функции ибо это отдельный компонент и компонент довольно маленький. Помести данную функцию в ./components/PasswordReset.js
Для того чтобы данная функция вообще имела смысл я создал отдельный компонент под названием PasswordReset. Создал файл-компонент PasswordReset.js в components и подключил его в index.js:
Вот полный код компонента:
Это ещё не всё. Потребуется ещё создание html-шаблона на стороне сервера и добавление соответствующих путей в urls.py
Форма и запрос на подтверждение почты
Так же моя система аутентификации поддерживает проверку почты на действительность. Пока смысла в этом особо нет, но в будущем это будет вполне полезно если я, например, хочу знать действительный ли это адресс электронной почты или нет.
Мы должны отправить PUT-запрос по адресу /_allauth/browser/v1/account/email или URLs.EMAIL.
На указанную почту придёт письмо с сылкой на страницу с ключом. Перейдя на данную страницу пользователь увидит следующее:
Где нужно только нажать на кнопку. Оптравкой формы подтверждения занимается вот эта функция:
В данном случае я не делал разделение на on* и fetch* функции ибо это отдельный компонент и компонент довольно маленький. Помести данную функцию в ./components/EmailVerify.js
Она отправляет ключ на сервер для подтверждения почты и возвращает соответствующее сообщение о статусе верификации.
Ровно как и в случае со сбросом пароля, я создал отдельный компонент и подключил его в index.js:
А вот и полный код компонента EmailVerify:
Это ещё не всё. Потребуется ещё создание html-шаблона на стороне сервера и добавление соответствующих путей в urls.py
Форма и запрос на выход
И наконец, выход из текущей сессии. Делается DELETE запрос по адресу /_allauth/browser/v1/auth/session или URLs.LOGOUT. Будет возвращена ошибка с кодом 401, поэтому выход делаем в catch блоке.
Настраиваем маршруты и представления для подтверждения почты и сброса пароля
Мы почти закончили. Осталось только настроить Бэкенд для подтверждения почты и изменения пароля. Создай новое django-приложение, если не знаешь как, смотри тут. Назови его Authentication ну и конечно подключи его к проекту сайта.
Дальше создай два шаблона в templates/Authentication, email_verify.html и password_reset.html
Содержание email_verify.html:
Содержание password_reset.html:
Как видишь, они почти идентичны. Впринципе, мог и один общий шаблон сделать, но подумал что возможно в будущем может потребоваться их как-то развивать по отдельности. Теперь добавим два маршрута в Authentication/urls.py:
Заметь пути совпадают с теми что я указывал в settings.py ранее. Осталось только создать соответствующие представления для данных шаблонов, email_verify и password_reset:
Здесь всё довольно прямолинейно, получаем ключ из URL, сохраняем его в контекст при рендеринге и вставляем key в качестве значения атрибута data-key в контейнерах. Дальше этот атрибут будет использовать Реакт для отправки обратных запросов.
Заключение
Я закончил добавлять систему аутентификации и регистрации моих гостей на сайте. Конечно, можно ещё добавить вход и регистрацию через социальные сети или на основе отправляемых ключей подтверждения. Всё это будет позже и возможно уже в других проектах, но точно не в этой статье, она и так получилась слишком большой.
Текущую и полную версию сайта-проекта ты можешь скачать здесь.
В следующей серии мы займёмся переводами нашего сайта на другие языки, ну а пока, до скорых встреч. ( ̄︶ ̄)↗