Это 13-я часть моей серии React Learning. Использование знаний, почерпнутых из React Веса Боса для начинающих, среди прочего.
После нашего небольшого отвлечения на Proptypes давайте застрянем на самых важных вещах, аутентификации и авторизации. Поясним, что я имею в виду:
Authentication - Checking to see who someone is
Authorisation - Controlling what can be accessed based upon who they are
Чтобы говорить об этом, нам нужен пример приложения, где мы его используем. Итак, давайте представим приложение, в котором у нас есть три основных компонента:
- Меню — где пользователи могут выбирать, что заказывать
- Заказ — где пользователи могут увидеть, что они заказали.
- Инвентарь — который диктует, что доступно для заказа
Они вложены в компонент приложения с уникальным идентификатором магазина. Предоставление пользователям доступа к их собственному экземпляру приложения.
В этом приложении мы хотим ограничить инвентарь владельцем. Это то, что мы собираемся сделать в этом посте, используя аутентификацию Firebase для входа в GitHub.
Большие темы быстро запутываются, поэтому тема этого поста — разбить все на более мелкие шаги…
- Настройте поставщиков аутентификации с помощью Firebase.
- Создайте наш компонент обработки входа в систему и создайте метод
authenticate
для запроса аутентификации у соответствующего поставщика входа. - Создайте метод «authhandler», чтобы делать что-то, как только мы получим результаты метода аутентификации.
- Настройте логику в компоненте Inventory для отображения соответствующего компонента авторизованным пользователям.
- Внедрите правила Firebase, чтобы эффективно заблокировать его в серверной части.
Итак, приступим…
Часть 1. Настройка наших поставщиков аутентификации
Этот раздел посвящен настройке серверной части, в нашем случае серверной частью является Firebase и сервисы, с которыми мы хотим пройти аутентификацию. Мы не будем заниматься созданием собственной аутентификации, так как это быстро запутается!
Для каждого поставщика услуг входа есть два основных шага:
- Получите ключи API и настройте параметры на сайте разработчика поставщика услуг входа.
- Настройте Firebase с данными API от поставщика входа
Примечание о получении ключей API
На самом деле я написал разделы о получении ключей API для аутентификации Facebook, GitHub и Twitter. С момента написания (2018 г.) они изменили способ работы. Что еще более важно, они, вероятно, изменят его снова к тому времени, когда вы будете читать это, так что вам лучше понять это из их собственной документации, а не следовать старым советам. Я, по крайней мере, включу GitHub как довольно простой пример того, что мы пытаемся сделать.
Ключ API GitHub
- Перейдите в вашу консоль Firebase и перейдите к
Authentication
- Выберите Github внутри
set up sign in method
- Теперь ему понадобятся
App ID
иApp Secret
. Также обратите внимание, что он предоставляетURL
для возврата после попытки аутентификации. - Перейдите к [https://github.com/settings/developers]
- Зарегистрируйте новое приложение OAuth и вставьте URL-адрес обратного вызова авторизации из firebase.
- Идентификатор клиента и секрет клиента теперь будут доступны для добавления в Firebase.
Часть 2 — Компонент входа и метод аутентификации
Итак, давайте рассмотрим настройку приложения для отправки запроса на вход. АКА Аутентификация. АКА «Кто ты?»
Нам нужно предоставить код для общения с Firebase. Это традиционно на странице входа в систему.
Основные шаги для этого:
- Настройте компонент входа
- Настройте родительский компонент инвентаризации с помощью метода аутентификации.
- Передайте метод аутентификации в логин через реквизит
- Настройте компонент входа для использования метода аутентификации
Настройка и тестирование компонента входа в систему
Компонент входа будет отображать некоторые кнопки, чтобы разрешить вход в провайдеров, которые мы настроили на шаге 1. Поскольку он мало что делает, мы можем просто сделать его функциональным компонентом без сохранения состояния.
В нашем компоненте Inventory нам нужно проверить, видим ли мы кнопку. Сначала импортируйте наш компонент входа:
import Login from "./Login"
А затем вставьте еще один возврат в начале метода рендеринга, эффективно перенаправляя компонент Inventory для отображения компонента входа в систему только на данный момент:
return <Login />
Как только мы будем счастливы, ничто глупое не помешает этой кнопке работать, давайте углубимся в методы, которые нам нужны, когда кнопка используется.
Заставить кнопку что-то делать… почти
Когда мы нажимаем кнопку, мы хотим, чтобы она начала процесс аутентификации для нужного нам провайдера входа. В оставшейся части этого поста я сосредоточусь на том, чтобы заставить работать вход Github, и я надеюсь, что вы сможете понять то же самое для других поставщиков входа.
Итак, наша кнопка должна:
- Вызовите метод в свойствах с именем authentication. Этого нет, но скоро будет.
- Предполагая, что у нас будет более одного типа кнопки входа… в качестве параметра для этого метода мы возьмем имя провайдера с заглавной буквы. Почему капитал? Я объясню это тоже немного позже, обещаю.
Теперь наша кнопка входа на GitHub в компоненте входа выглядит так:
<button onClick={() => props.authenticate('GitHub')}> Sign in with GitHub </button>
Обратите внимание, что в функциональном компоненте мы ссылаемся на реквизиты, передавая параметр, а не используя this
.
Также не забудьте добавить PropTypes, например:
Login.propTypes = { authenticate: Proptypes.func.isRequired}
Создание метода аутентификации
Поскольку аутентификация не требует включения состояния на верхнем уровне, мы можем написать метод аутентификации на уровне компонента Inventory, который может решить, нужно ли отображать компонент входа в систему.
Во-первых, убедитесь, что вы импортировали FireBase, иначе мало что произойдет, в Firebase есть методы, которые нам нужны, чтобы упростить эту задачу:
import firebase from 'firebase'
;
Нам также нужно сослаться на наш базовый компонент (обратитесь к моему сообщению о сохранении данных для получения дополнительной информации об этом), который содержит детали API:
import {firebaseApp} from '../base'
- Для метода мы передаем значение с кнопки как
Provider
(т.е. «GitHub»). Это умный способ проверить провайдера регистрации. - Указываем нашего провайдера авторизации
- Мы используем методы Firebase для создания всплывающего окна, которое использует поставщика аутентификации, указанного для запроса входа в систему.
Код выглядит примерно так:
Часть 3. Обработка ответа аутентификации
Последняя строка этого фрагмента кода вызывает authHandler
, который обрабатывает то, что делает наше приложение после возврата данных аутентификации.
Если пока все шло хорошо. Теперь вы сможете войти в GitHub и увидеть объект, возвращенный в консоль. Этот объект содержит всю необходимую нам информацию.
Теперь эта часть начинает становиться очень специфичной для случая использования, поэтому я могу потерять вас здесь, но главное заключается в том, что объект authData дает нам информацию, которую мы можем сравнить с другой информацией, чтобы определить, что должно произойти. Это и есть аутентификация в двух словах.
В нашем случае использования authhandler необходимо сделать три вещи:
- Найдите текущий магазин в базе данных Firebase.
- Установите владельца в качестве текущего пользователя, если владельца нет
- Установите состояние компонента инвентаризации так, чтобы оно отражало текущего пользователя, чтобы теперь он знал, кто вошел в систему.
1. Найдите текущий магазин в базе данных Firebase.
В нашем приложении нам нужно найти текущий магазин и посмотреть, есть ли владелец, для этого нам нужно посмотреть нашу базу данных, поэтому импортируйте базу из нашего базового компонента:
import base, {firebaseApp} from '.../base'
Затем нам нужно получить информацию о текущем магазине. Но подождите…, сначала нам нужно, чтобы компонент Inventory знал storeid, который мы можем получить от родительского компонента App (который сам получает его от React Router):
storeId ={this.props.match.query.storeId}
Итак, вернемся к нашему методу authHandler в компоненте Inventory для получения хранилища из Firebase:
const store = await base.fetch( *STORE GOES HERE* )
Чтобы получить имя магазина, нам нужно передать реквизит из приложения:
storeId={this.props.match.params.storeId}
Итак, готовая команда в authhandler выглядит так:
const store = await base.fetch(this.props.storeId, {context: this})
Если мы не используем await здесь, store будет обещанием, а не результатом обещания, которое нам нужно.
2. Заявить право собственности, если нет текущего владельца
Далее нам нужно проверить, есть ли владелец, если нет, мы сохраняем информацию о владельце в БД Firebase.
На этом этапе мы должны иметь возможность проверить в базе данных Firebase, установлен ли владелец.
3. Установите состояние компонента инвентаризации, чтобы отразить текущего пользователя
Чтобы решить, что делать, когда пользователь входит в систему, нам нужно знать две вещи:
- Что такое UID вновь вошедшего в систему пользователя?
- Кто является владельцем ресурса? Если есть?
Команда setState выглядит так:
Поскольку нам не нужна эта информация где-либо еще, мы можем установить State локально для компонента Inventory, а не для родительского компонента приложения. Для этого требуется настроить состояние компонента:
Весь метод authHandler выглядит так:
Если все прошло хорошо, вы сможете войти в систему и увидеть новые ключи в состоянии Inventory и значение владельца в хранилище Firebase DB.
Часть 4 — Отображение правильного контента
Методу рендеринга Inventory потребуется некоторая логика для проверки следующих сценариев:
- ЕСЛИ они НЕ вошли в систему, ТО показать компонент входа
- ЕСЛИ вы вошли в систему, а НЕ владелец, ТО показать «Сообщение об отсутствии доступа»
- ЕСЛИ вы вошли в систему И являетесь владельцем, ТО показать компонент инвентаря
- Помимо этого, мы также хотим сделать кнопку выхода из системы, чтобы разрешить другой вход в систему, если это необходимо.
- Наконец, мы не хотим входить в систему каждый раз при обновлении страницы, поэтому давайте автоматически перепроверим текущего пользователя.
1. Если НЕ вошел в систему, ТО показать компонент входа
UID, содержащийся в State, определяет, вошли ли они в систему, поэтому нам просто нужно вернуть компонент входа, если uid недоступен:
if (!state.uid) {return <Login authenticate={this.authenticate} />}
2. Вы вошли в систему, но НЕ являетесь владельцем, ТОГДА показать что-то типа «У вас нет доступа»
Достаточно просто, нам нужно сравнить uid в состоянии с владельцем в состоянии:
if (this.state.uid !== this.state.owner){return <div>You are not the owner</div>}
Вы, вероятно, захотите украсить это лучшим откликом, чем простой div, но пока этого достаточно.
3. Вы вошли в систему И являетесь владельцем, ТОГДА показать компонент инвентаризации.
Если они проходят первые два теста, мы знаем, что они являются владельцами и могут вернуть компонент как обычно.
4. Кнопка «Выйти»
Почти готово, в идеале это должен быть компонент, но для краткости мы определим переменную JSX внутри метода рендеринга:
const logout = <button onClick={this.logout}>Log Out</button>
Затем мы можем разместить кнопку на путях возврата «не владелец» и «владелец».
Метод выхода
Итак, давайте разберемся с методом выхода из системы, есть две задачи, которые необходимо выполнить во время выхода:
- Выйти из провайдера аутентификации
- Очистить состояние текущих сведений о пользователе
Это можно сделать в каждой строке:
https://gist.github.com/6e85d040afc6099bcc6a5f72f7899de9
5. Перепроверив, мы вошли в систему
Когда мы обновляем страницу, было бы хорошо, если бы она могла проверить, вошли ли мы в систему, чтобы избежать запроса на вход. Нам просто нужно использовать метод жизненного цикла componentDidMount
, чтобы:
- Получите Firebase, чтобы проверить, есть ли пользователь
- Передайте этого пользователя нашему методу authHandler
https://gist.github.com/e33a91f614ab1920a004b0abe3d530ca
Уф, об этом нужно много думать, но, надеюсь, вы видите, что, разбив его на маленькие шаги, это немного сводит с ума.
Часть 5 — Защита серверной части Firebase
Все, что мы сделали до сих пор, защитило клиентскую сторону, решительный человек все еще может получить доступ и изменить информацию в Firebase, поскольку мы оставили ее открытой для чтения и записи для всех (если вы следили за моим предыдущим постом о Firebase)
К счастью для нас, это довольно просто сделать:
- Перейти в Firebase
- Перейдите в раздел
Database
, а затемRules
Это должно привести вас к разделу правил базы данных, где ранее мы разрешали чтение и запись как true:
Вместо этого нам нужно изменить это следующим образом:
Давайте объясним, что:
- Доступ для чтения разрешен для всех в любом месте.
- Пользователь может писать только на верхнем уровне (т. е. куда идет хранилище), если нет текущего хранилища (т. е. данные не существуют).
- В магазине ($room) запись разрешена только в том случае, если:
- auth не является нулевым (пользователь вошел в систему)
- Либо данные отсутствуют, либо существующий владелец совпадает с текущим.
Вывод
Я уже писал об аутентификации с помощью Passport JS. В целом это кажется немного более дружелюбным, поскольку Firebase берет на себя часть тяжелой работы. Кроме того, использование async и await позволяет избежать ада обратных вызовов, с которым я сталкивался раньше.
Надеюсь, вышеизложенного достаточно, чтобы запустить аутентификацию, когда это необходимо. Однако я думаю, что некоторое время с документами Firebase при попытке использовать его было бы очень хорошей идеей.
Разбить его на небольшие шаги — это определенно способ не сойти с ума, когда дело доходит до аутентификации.