Использование JWT в приложении React + Redux для авторизации

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

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

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

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

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

!!!Погнали!!!

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

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

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

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

Новая форма пользователя

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

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

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

Функцию с fetch request
В вашем файле actions.js он будет выглядеть примерно так:

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

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

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

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

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

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

Сохранити объект пользователя в вашем хранилище(store) Redux.

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

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

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

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

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

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

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

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

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

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

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

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

3. Пересмотр пользователя (GET to / profile)

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

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

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

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

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

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

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

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

И вот он у вас — у вас есть действующее разрешение!

Выход

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

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

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

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

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

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

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

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

--

--

Iurii Kalashnikov
FreeCodeCamp Russia(Русскоязычный)

Программист, бразильское джиу джитсу. https://github.com/YKalashnikov давайте дружить