Объяснение и руководство по работе с JWT и аутентификацией во внешнем интерфейсе вашего веб-приложения.

«Как Auth работает с Redux и JWT?»

Общая идея заключается в том, что вы сохраните информацию о текущем пользователе в магазине Redux для быстрого доступа через ваше приложение. Вы также сохраните JWT (веб-токен JSON), связанный с пользователем, в localStorage, чтобы его вход в систему мог сохраняться между сеансами, если они явно не выходят из системы.

В этом руководстве предполагается, что у вас настроены три следующих маршрута Rails API:

POST to / users
POST to / login
GET to / profile

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

Давайте начнем!

Примечание: это руководство в основном предназначено для студентов Mod 5 Bootcamp по разработке программного обеспечения Flatiron School, которые внедрили аутентификацию JWT в свой бэкэнд Rails API, но не знают, как заставить их клиентское приложение React + Redux обрабатывать аутентификацию.

Надеюсь, я смогу помочь прояснить рабочий процесс Auth и то, как применить его к вашему проекту!



1. Регистрация пользователей (POST для / пользователей)

Когда новый пользователь посещает ваше приложение, вы можете захотеть, чтобы он зарегистрировал учетную запись. По сути, вы запустите стандартный запрос POST; вам не нужно делать ничего особенного, чтобы все заработало.

При правильной настройке ваш бэкэнд создаст экземпляр пользователя, введет пароль с помощью BCrypt, а затем вернет объект с ключом пользователя и ключом токена. Этот объект является важной частью Auth. Вы увидите это позже в этом руководстве, но мы, по сути, возьмем объект пользователя и сохраним его в вашем хранилище Redux, а затем возьмем токен, связанный с пользователем, и сохраним его в localStorage.

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

Создайте форму для отправки нового пользователя

В вашем приложении React вам понадобится форма, которая после отправки будет запускать ваш выбор в вашем actions.js файле. Здесь вы будете использовать преобразователь Redux, поэтому убедитесь, что он у вас установлен.

Создайте управляемый компонент, который представляет собой форму для создания нового пользователя. Например, это может выглядеть так:

Обратите внимание на то, что некоторая неизвестная функция с именем userPostFetch импортируется из actions.js, а затем добавляется в качестве опоры к компоненту с помощью mapDispatchToProps. Вы можете видеть выше, что это свойство вызывается при отправке формы. Это будет функция, которая обрабатывает саму выборку, а также сохраняет пользовательский объект в хранилище Redux и добавляет токен в localStorage. Далее мы напишем эту функцию.

Есть функция с запросом на выборку

В вашем actions.js файле это будет выглядеть примерно так:

Обратите внимание на две отдельные функции: userPostFetch и loginUser. Функция userPostFetch отправляет информацию о пользователе на ваш сервер для проверки. В случае успеха ожидается ответ объекта JSON, который выглядит следующим образом:

{
  user: {
    username: "ImANewUser",
    avatar: "https://robohash.org/imanewuser.png",
    bio: "A new user to the app."
  },
  token: "aaaaaaa.bbbbbbbb.ccccccc"
}

В userPostFetch это то, что мы назвали «данными» во втором операторе «затем».

Сохраните токен в localStorage

С помощью кода, который мы написали в нашей userPostFetch функции, localStorage.setItem(“token”, data.token) сохранит токен (“aaaaaaa.bbbbbbbb.ccccccc”) в локальном хранилище нашего пользователя. Это будет использоваться позже, когда мы сохраняем логин пользователя между сеансами.

Чтобы убедиться, что токен был успешно сохранен, запустите localStorage.token или localStorage.getItem(“token”) в консоли.

Сохраните пользовательский объект в свой магазин Redux

Что касается объекта пользователя, здесь мы видим, что dispatch(loginUser(data.user)) запущен. Предположительно, ваш редуктор примет пользовательский объект ({username: “ImANewUser”}) и сохранит его в вашем хранилище Redux. Это позволит любому компоненту вашего приложения React легко узнать, кто является текущим пользователем.

В качестве примера вот мой редуктор:

Здесь пользовательский объект (action.payload) сохраняется в состояние под ключом currentUser. Если у вас установлен Redux DevTools, вы можете проверить его после успешного создания вашего пользователя. Вы должны увидеть объект пользователя.

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

Если вы решили это сделать, просто ничего не делайте с пользовательским объектом и токеном JWT. Фактически, вы можете редактировать логику из своего бэкэнда, чтобы вместо этого он возвращал только статусные сообщения.

Это все, что нужно для регистрации нового пользователя. Далее мы узнаем, как войти в систему существующему пользователю.

2. Вход пользователей в систему (POST to / login)

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

Если у вас есть компонент, предназначенный для входа в систему, он будет похож на ваш компонент регистрации с одним существенным отличием - он будет импортировать другую функцию из вашего actions.js файла. Это может выглядеть примерно так:

Мы еще не написали функцию userLoginFetch, но, опять же, ее внешний вид похож на выборку, которая обрабатывала регистрацию. См. ниже:

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

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

Теперь займемся третьей и последней частью: сохраним логин вашего пользователя между сеансами.

3. Повторные посещения пользователей (ПЕРЕЙТИ в / profile)

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

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

Для этого вам нужно будет запускать выборку (GET to / profile) каждый раз при обращении к вашему приложению, если у пользователя есть токен, сохраненный в их localStorage. Запуск этой логики в componentDidMount в вашем компоненте приложения - хороший выбор, поскольку он определенно будет запускаться при доступе к вашему приложению.

Мы импортируем функцию из actions.js под названием getProfileFetch, которая запускается сразу после монтирования компонента приложения.

getProfileFetch будет выполнять стандартный запрос GET, за исключением заголовка авторизации с токеном, который вы обрабатываете в своем actions.js файле. Ваш бэкэнд должен быть настроен для получения токена, его декодирования и последующего возврата связанного с ним пользовательского объекта. Затем вы сохраняете это в магазине Redux как обычно. У вас уже есть токен, сохраненный в localStorage, поэтому вам не о чем беспокоиться.

Функция будет выглядеть примерно так:

Функция getProfileFetch сначала проверяет, сохранен ли токен в localStorage, прежде чем пытаться сохранить логин. Таким образом, вы не будете запускать ненужную выборку.

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

Вот и все - авторизация работает!

*** БОНУС: выход из системы

По мере тестирования приложения вы заметите, что в настоящее время единственный способ выйти из системы - это очистить токен JWT в вашем локальном хранилище, набрав localStorage.removeItem(“token”) в консоли и нажав кнопку «Обновить», чтобы очистить хранилище Redux. Мы должны создать кнопку, чтобы ваши клиенты могли выйти из системы.

Где-нибудь в вашем приложении вам понадобится кнопка выхода, которая сделает то, что описано выше. Хотя я бы не стал размещать это случайным образом в файле App.js, вот пример кнопки выхода в App.js для примера.

Вы должны заметить несколько новых вещей: во-первых, импортируется новое действие с именем logoutUser, о котором мы вскоре напишем. У нас также теперь есть mapStateToProps, используемый для того, чтобы компонент App получил свойство с именем currentUser.

Примечание. Поскольку у меня есть несколько редукторов, мне пришлось получить доступ к текущему пользователю моего магазина Redux, набрав state.reducer.currentUser в mapStateToProps. Если у вас только один редуктор, вам, скорее всего, нужно будет написать только state.currentUser.

Теперь существует также тернарный оператор, который проверяет, имеет ли свойство currentUser, полученное из хранилища Redux, ключ имени пользователя (например, если объект currentUser пуст или нет). Если это так, отображается кнопка «Выйти», которая вызывает logoutUser при нажатии. Он также удалит токен из localStorage.

logoutUser будет простым действием:

Это действие выполнит следующее в редукторе, заменив currentUser пустым объектом:

И вот оно! Вы должны увидеть, как кнопка появляется и исчезает, когда вы входите в систему и выходите из нее.

Как проверить?

Возможно, вы следовали вышеизложенному и сомневаетесь, работает ли ваша авторизация. Вы можете проверить это, зарегистрировавшись, войдя в систему, нажав кнопку «Обновить» и выйдя из системы. Если все работает должным образом, вы должны увидеть объект пользователя, сохраненный в вашем хранилище Redux под ключом currentUser с помощью Redux DevTools, и токен, сохраненный в вашем локальном хранилище, который вы можете просмотреть в консоли.

Что теперь?

Что касается того, куда идти дальше, вы можете выполнить проверки в своих компонентах, чтобы по существу выгнать пользователя, если он не вошел в систему. Вы можете сделать это с помощью Redirect из react-router-dom или функции push из connected-react-router. Это ресурсы, которые я предпочитаю использовать.

Теперь вы также можете передать свой объект currentUser из вашего хранилища Redux любому компоненту в вашем приложении, как мы видели ранее. Например, если вы хотите, чтобы ваша панель навигации отображала аватар пользователя, вы можете использовать mapStateToProps для передачи объекта currentUser в панель навигации, а затем визуализировать аватар.

На этом пока все - спасибо за чтение и удачного кодирования!