Потоки аутентификации¶
В большинстве приложений для получения доступа к данным, связанным с пользователем, или другому приватному контенту требуется аутентификация пользователя. Как правило, процесс выглядит следующим образом:
- Пользователь открывает приложение.
- Приложение загружает некоторое состояние аутентификации из зашифрованного постоянного хранилища (например,
SecureStore
). - После загрузки состояния пользователю открываются либо экраны аутентификации, либо основное приложение, в зависимости от того, было ли загружено действительное состояние аутентификации.
- Когда пользователь выходит из системы, мы очищаем состояние аутентификации и возвращаем его к экранам аутентификации.
Примечание
Мы говорим "экраны аутентификации", потому что обычно их несколько. У вас может быть основной экран с полем для ввода имени пользователя и пароля, другой экран для "забыл пароль" и еще один экран для регистрации.
Что нам нужно¶
Мы хотим, чтобы поток аутентификации вел себя следующим образом: при входе пользователя в систему мы должны отбрасывать состояние потока аутентификации и размонтировать все экраны, связанные с аутентификацией, а при нажатии аппаратной кнопки "назад" мы не должны иметь возможности вернуться к потоку аутентификации.
Как это будет работать¶
Мы можем определить различные экраны на основе некоторого условия. Например, если пользователь вошел в систему, мы можем определить Home
, Profile
, Settings
и т.д. Если пользователь не вошел в систему, мы можем определить экраны SignIn
и SignUp
.
Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
При таком определении экранов, когда isSignedIn
имеет значение true
, React Navigation будет видеть только экраны Home
, Profile
и Settings
, а когда false
, React Navigation будет видеть экраны SignIn
и SignUp
. Это делает невозможным переход к экранам Home
, Profile
и Settings
, когда пользователь не вошел в систему, и к экранам SignIn
и SignUp
, когда пользователь вошел в систему.
Этот паттерн уже давно используется в других библиотеках маршрутизации, таких как React Router, и известен как "защищенные маршруты". В данном случае экраны, требующие входа пользователя в систему, "защищены" и не могут быть открыты другими способами, если пользователь не вошел в систему.
Волшебство происходит при изменении значения переменной isSignedIn
. Допустим, изначально isSignedIn
имеет значение false
. Это означает, что на экране отображается либо SignIn
, либо SignUp
. После того как пользователь зарегистрируется, значение isSignedIn
изменится на true
. React Navigation увидит, что экраны SignIn
и SignUp
больше не определены, и удалит их. Затем он автоматически покажет экран Home
, поскольку это первый экран, определенный, когда isSignedIn
имеет значение true
.
В примере показан стековый навигатор, но вы можете использовать тот же подход с любым навигатором.
Условно определяя различные экраны на основе переменной, мы можем реализовать поток авторизации простым способом, не требующим дополнительной логики для обеспечения правильного отображения экрана.
При условной визуализации экранов не нужно осуществлять ручную навигацию¶
Важно отметить, что при такой настройке не нужно вручную переходить к экрану Home
, вызывая navigation.navigate('Home')
или любой другой метод. React Navigation будет автоматически переходить на нужный экран при изменении isSignedIn
- на экран Home
, когда isSignedIn
становится true
, и на экран SignIn
, когда isSignedIn
становится false
. При попытке навигации вручную вы получите ошибку.
Определяем экраны¶
В нашем навигаторе мы можем условно определить соответствующие экраны. В нашем случае, допустим, у нас есть 3 экрана:
SplashScreen
- будет показывать заставку или экран загрузки, когда мы восстанавливаем токен.SignInScreen
- этот экран мы показываем, если пользователь еще не вошел в систему (мы не смогли найти токен).HomeScreen
- это экран, который мы показываем, если пользователь уже вошел в систему.
Таким образом, наш навигатор будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
В приведенном выше фрагменте isLoading
означает, что мы все еще проверяем, есть ли у нас токен. Обычно это можно сделать, проверив наличие токена в SecureStore
и подтвердив его. После того как мы получили токен и убедились, что он действителен, нам нужно установить userToken
. У нас также есть еще одно состояние isSignout
, чтобы иметь другую анимацию при выходе из системы.
Главное, что следует отметить, - это то, что мы условно определяем экраны на основе этих переменных состояния:
- экран
SignIn
определяется только в том случае, еслиuserToken
равенnull
(пользователь не вошел в систему) - Экран
Home
определяется только в том случае, еслиuserToken
не равенnull
(пользователь вошел в систему).
Здесь мы условно определяем один экран для каждого случая. Но можно определить и несколько экранов. Например, вы, вероятно, захотите определить экраны сброса пароля, регистрации и т.д., когда пользователь не вошел в систему. Аналогично, для экранов, доступных после входа в систему, вероятно, потребуется несколько экранов. Мы можем использовать React.Fragment
для определения нескольких экранов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
Если экраны, связанные с входом в систему, и остальные экраны находятся в двух разных навигаторах Stack, мы рекомендуем использовать один навигатор Stack и поместить в него условие, а не использовать два разных навигатора. Это позволит получить корректную анимацию перехода при входе/выходе.
Реализуем логику восстановления токена¶
Примечание
Ниже приведен пример того, как можно реализовать логику аутентификации в вашем приложении. Не обязательно следовать ему в точности.
Из предыдущего фрагмента видно, что нам нужны 3 переменные состояния:
isLoading
- мы устанавливаем это значение вtrue
, когда пытаемся проверить, сохранен ли уже токен вSecureStore
.isSignout
- Мы устанавливаем значениеtrue
, когда пользователь выходит из системы, в противном случае устанавливаем значениеfalse
.userToken
- Токен пользователя. Если он не равенnull
, мы считаем, что пользователь вошел в систему, в противном случае - нет.
Таким образом, нам необходимо:
- Добавить логику для восстановления токена, входа и выхода из системы
- Предоставить методы для входа и выхода другим компонентам.
В данном руководстве мы будем использовать React.useReducer
и React.useContext
. Но если вы используете библиотеку управления состоянием, такую как Redux или Mobx, вы можете использовать их для этой функциональности. На самом деле, в больших приложениях для хранения токенов аутентификации больше подходит глобальная библиотека управления состоянием. Вы можете применить тот же подход к своей библиотеке управления состояниями.
Сначала нам нужно будет создать контекст для auth
, в котором мы сможем открыть необходимые методы:
1 2 3 |
|
Таким образом, наш компонент будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
|
Заполнение других компонентов¶
Мы не будем говорить о том, как реализовать текстовые вводы и кнопки для экрана аутентификации, это выходит за рамки навигации. Мы просто заполним их содержимым.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
|
Удаление общих экранов при изменении состояния авторизации¶
Рассмотрим следующий пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Здесь у нас есть специальные экраны, такие как SignIn
, Home
и т.д., которые показываются только в зависимости от состояния входа в систему. Но у нас также есть экран Help
, который может быть показан в обоих случаях. Это также означает, что если состояние входа изменится, когда пользователь находится на экране Help
, то он останется на экране Help
.
Это может быть проблемой, поскольку мы, вероятно, хотим, чтобы пользователь переходил на экран SignIn
или Home
, а не оставался на экране Help
. Для того чтобы это работало, мы можем использовать свойство navigationKey
. Когда navigationKey
изменится, React Navigation удалит все экраны.
Таким образом, наш обновленный код будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Если у вас есть несколько общих экранов, вы также можете использовать navigationKey
с Group
для удаления всех экранов в группе. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|