Да, давно я не писал про этот сайт. И теперь считаю своим долгом закончить его во чтобы-то ни стало. Итак, нужно разработать и добавить систему аутентификации и регистрации пользователей на сайт. Как это сделать и как это будет выглядеть?
Здесь на самом деле нет ничего сложного. Сам подумай, нам необходимо разработать следующие элементы:
Форму входа
Форму регистрации
Форму изменения пароля
Форму выхода
Форму подтверждения почты + шаблон
Форму сброса пароля + шаблон
Модальное окно для профиля пользователя
И, собственно говоря, всё ... Хотя 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 в контейнерах. Дальше этот атрибут будет использовать Реакт для отправки обратных запросов.
Заключение
Я закончил добавлять систему аутентификации и регистрации моих гостей на сайте. Конечно, можно ещё добавить вход и регистрацию через социальные сети или на основе отправляемых ключей подтверждения. Всё это будет позже и возможно уже в других проектах, но точно не в этой статье, она и так получилась слишком большой.
Текущую и полную версию сайта-проекта ты можешь скачать здесь.
В следующей серии мы займёмся переводами нашего сайта на другие языки, ну а пока, до скорых встреч. ( ̄︶ ̄)↗
В этой статье ты узнаешь как создать Django приложение и настроить его для работы с React JS чтобы получилось полноценное фулстак приложение. Так же в статье приведено видео-туториал и скачиваемые …
Показываю и рассказываю о том как разработать фронтен для сайта на Реакте с бэкендом на django. Использую MaterialUI и TailwindCSS, с исходным кодом и комментариями.
Использованные термины
Django шаблон ⟶ Это текстовый документ, который размечен специальным ситнаксисом для вставки кода в него.
Реакт ⟶ Это библиотека JavaScript, разработанная Facebook для создания пользовательских интерфейсов, в частности, для одностраничных приложений. React позволяет разработчикам создавать компоненты, которые могут эффективно обновлять и рендерить при изменении данных.
Реакт компонент ⟶ Это независимый, переиспользуемый фрагмент кода, который отвечает за определённый участок пользовательского интерфейса (UI). Компоненты могут быть созданы как классовые, так и функциональные.
CORS (Cross-Origin Resource Sharing) ⟶ Это механизм, который позволяет ограничить ресурсы веб-страниц, чтобы они могли запросить ресурсы с других доменов. Поскольку правила безопасности браузеров по умолчанию блокируют такие запросы (из-за политики одинаковых источников), CORS предоставляет способ более безопасного доступа к ресурсам с других источников.
Релевантные вопросы
Для чего нужен атрибут key при рендере списков?
Ключи (keys) помогают React определять, какие элементы были изменены, добавлены или удалены. Их необходимо указывать, чтобы React мог сопоставлять элементы массива с течением времени. Лучший способ выбрать ключ — это использовать строку, которая будет явно отличать элемент списка от его соседей. Чаще всего вы будете использовать ID из ваших данных как ключ.
Как работает проп children?
Некоторые компоненты не знают своих потомков заранее. Это особенно характерно для таких компонентов, как Sidebar или Dialog, которые представляют из себя как бы «коробку», в которую можно что-то положить. Для таких компонентов мы рекомендуем использовать специальный проп children, который передаст дочерние элементы сразу на вывод
В чем разница между управляемыми и неуправляемыми компонентами?
В управляемом компоненте с каждой мутацией состояния связана функция-обработчик. Благодаря этому валидация или изменение введённого значения становится простой задачей. Неуправляемые компоненты опираются на DOM в качестве источника данных и могут быть удобны при интеграции React с кодом, не связанным с React.
Можно ли создавать анимации в React?
React может использоваться для создания крутых анимаций! В качестве примера посмотрите библиотеки React Transition Group и React Motion.