1. Предпосылки
Прежде всего, поговорим о том, как использовать возможности WeChat по идентификации пользователя при разработке мини-программ?
Официальный представитель WeChat предоставляет два вида логотипов:
-
OpenId
Это идентификатор пользователя для небольшой программы/общедоступной учетной записи, и разработчики могут идентифицировать пользователя с помощью этого идентификатора. -
UnionId
Это идентификация пользователя для одного и того же субъекта WeChat апплета/общедоступной учетной записи/приложения, и разработчику необходимо связать предмет той же учетной записи на открытой платформе WeChat. Разработчики могут использоватьUnionId
, чтобы обеспечить обмен данными между несколькими мини-программами, официальными учетными записями и даже приложениями.
Два идентификатора одного и того же пользователя являются постоянными для одного и того же апплета.Даже если пользователь удалит апплет, при следующем входе в апплет разработчик все равно сможет идентифицировать его по записям в фоновом режиме. Итак, как получитьOpenId
а такжеUnionId
Шерстяная ткань?
Раннее программирование раннего (апрель 2018 г.)wx.getUserInfo
Интерфейс для получения информации о пользователе. Первоначальная цель разработки этого интерфейса — надеяться, что разработчики смогут вызывать этот интерфейс только тогда, когда им действительно нужна информация о пользователе (например, аватар, псевдоним, номер мобильного телефона и т. д.). Но многие разработчики для того, чтобы получитьUnionId
, этот интерфейс будет вызываться непосредственно при запуске апплета, вызывая проблемы у пользователей при использовании апплета. Это сводится к следующим пунктам:
- Разработчик напрямую обращается к домашней странице апплета
wx.getUserInfo
Авторизация и всплывающее окно для получения информации о пользователе заставят некоторых пользователей нажать кнопку «Отклонить». - Если разработчик не принимает меры по поводу отказа пользователя от всплывающего окна, пользователь должен авторизовать псевдоним аватара и другую информацию, чтобы продолжить использование апплета, что заставит некоторых пользователей отказаться от использования апплета.
- У пользователей нет хорошего способа повторной авторизации.Хотя WeChat официально добавил страницу настроек, позволяющую пользователям выбирать повторную авторизацию, многие пользователи не знают, что они могут это сделать.
Чиновники WeChat также знают об этой проблеме и обновили три возможности для получения информации о пользователях:
- Используйте компоненты для получения информации о пользователе.
-
Если пользователь соответствует определенным условиям, вы можете использовать
wx.login
Полученный код напрямую изменяется наunionId
. -
wx.getUserInfo
нет зависимостейwx.login
данные можно назвать.
В этой статье в основном рассказывается о второй способности, официальной WeChat.Поощряйте разработчиков получать разумный доступ, не беспокоя пользователейunionid
, и просить пользователя использовать псевдоним аватара только в случае необходимости, который выводит понятия «автоматический вход» и «вход пользователя».
2. Что такое автоматический вход?
Апплет может легко получить идентификатор пользователя, предоставленный WeChat, с помощью возможности входа в систему, предоставленной официальным лицом WeChat, и быстро установить пользовательскую систему в апплете.
Многие разработчики будутwx.login
а такжеwx.getUserInfo
Связанный вызов используется как логин, по сутиwx.login
Вход завершен,wx.getUserInfo
Просто получите дополнительную информацию о пользователе.
существуетwx.login
получатьcode
После этого он будет отправлен на бэкенд разработчика, а бэкенд разработчика через интерфейс пойдет на бэкенд WeChat в обмен наopenid
а такжеsessionKey
(сейчас будетunionid
также вернуть), поставить пользовательский статус входа3rd_session
(Этот бизнес называетсяauth-token
) возвращается к внешнему интерфейсу, и поведение при входе завершено.wx.login
Поведение тихое, без авторизации, и пользователь не заметит.
wx.getUserInfo
Он существует только для предоставления более качественных услуг, таких как получение номера мобильного телефона пользователя для регистрации в качестве участника или отображение псевдонима аватара и определение пола.unionId
Объедините с существующими портретами пользователей в других официальных учетных записях, чтобы получить исторические данные.Таким образом, разработчику не нужно принудительно выполнять авторизацию при первом входе пользователя в апплет..
2.1 Последовательность процесса автоматического входа в систему
официально даноwx.login
Лучшие практики заключаются в следующем:
Английская аббревиатура автоматического входа в системуsilentLogin
, код выглядит так:
private async silentLogin(): Promise<void> {
try {
this.status.silentLogin.ing();
// 获取临时登录凭证code
const code = await getWxLoginCode();
// 将code发送给服务端
const res = await API.login(code);
// 保存登录信息,如auth-token
storage.setSync(constant.STORAGE_SESSION_KEY, res.data);
this.status.silentLogin.success();
} catch (error) {
logger.error('静默登录失败', error);
this.status.silentLogin.fail(error);
throw error;
}
}
Его можно свести к следующим трем шагам:
- вызов апплета
wx.login()
Получатьвременные учетные данные для входаcode
И обратно на сервер разработчиков. - вызов на стороне сервера
auth.code2Session
интерфейс, в обмен наУникальный идентификатор пользователяOpenID
а такжесеансовый ключsession_key
. - Сервер разработчика может быть создан на основе идентификатора пользователя.Пользовательское состояние входа (например:
auth-token
), используемый для интерфейсных и внутренних взаимодействий в последующей бизнес-логикеИдентифицировать пользователя.
2.2 Проверка данных разработчика и расшифровка открытых данных
После успешного автоматического входа сервер WeChat выдастsession_key
На сервер, и это будет использоваться, когда вам нужно получить открытые данные WeChat.
Чтобы обеспечить безопасность пользовательских данных, возвращаемых открытым интерфейсом, WeChat будет подписывать данные в виде открытого текста. Разработчики могут выполнять проверку подписи пакетов данных в соответствии с потребностями бизнеса для обеспечения целостности данных.
- Апплет вызывает интерфейс (например,
wx.getUserInfo
) при получении данных, если пользователь прошел авторизацию, интерфейс одновременно вернет следующие поля. Если пользователь не авторизован, сначала появится всплывающее окно пользователя. Пользователь щелкает, чтобы согласиться с авторизацией, и интерфейс одновременно возвращает следующие поля. И наоборот, если пользователь отказывает в авторизации, вызов завершится ошибкой.
Атрибуты | Типы | иллюстрировать |
---|---|---|
userInfo |
UserInfo | Информационный объект пользователя, не содержитopenid и другую конфиденциальную информацию |
rawData |
string | Строка необработанных данных, за исключением конфиденциальной информации, используемой для расчета подписи. |
signature |
string | Используйте sha1 (rawData + sessionkey), чтобы получить строку для проверки информации о пользователе. |
encryptedData |
string | Зашифрованные данные с полной информацией о пользователе, включая конфиденциальные данные |
iv |
string | Начальный вектор для алгоритма шифрования |
cloudID |
string | Облачный идентификатор, соответствующий конфиденциальным данным, будет возвращен только тогда, когда апплет, разработанный в облаке, будет активирован, а открытые данные могут быть получены непосредственно через облачный вызов. |
- разработчики будут
signature
,rawData
Отправлено на сервер разработчика для проверки. Сервер использует соответствующий пользовательскийsession_key
Подпись вычисляется по тому же алгоритмуsignature2
,Сравнениеsignature
а такжеsignature2
Целостность данных можно проверить. Сервер разработчика сообщает интерфейсному разработчику, что данные заслуживают доверия, а информацию о пользователях можно безопасно использовать. - Если разработчик хочет получить конфиденциальные данные (такие как OpenID, UniodId), то
encryptedData
а такжеiv
Отправлено на сервер разработчика, используется серверомsession_key
(симметричный ключ дешифрования) дляСимметричное дешифрование, чтобы получить конфиденциальные данные для хранения и вернуть разработчикам интерфейса.
Уведомление:Поскольку пользователю необходимо активировать триггер, чтобы инициировать получение интерфейса номера мобильного телефона, эта функция не вызывается API (т.wx.getUserInfo
номер мобильного телефона не может быть получен), необходимо использоватьbutton
Компонент нажмите, чтобы вызвать. получатьencryptedData
а такжеiv
, также отправляется на сервер разработчика, используется серверомsession_key
(симметричный ключ дешифрования) дляСимметричное дешифрованиеполучить соответствующий номер мобильного телефона.
Следует отметить, что 23 февраля 2021 года команда WeChat выпустила«Вход в мини-программу, инструкции по настройке интерфейса, связанные с пользовательской информацией», со следующими корректировками:
- С 23 февраля 2021 г. по
wx.login
Учетные данные для входа, полученные через интерфейс, могут быть обменены напрямую.unionID
. - Новые версии мини-программ, выпущенные после 13 апреля 2021 г., не могут пройти
wx.getUserInfo
Интерфейс получает личную информацию пользователя (аватар, псевдоним, пол и регион) и будет напрямую получить анонимные данные.getUserInfo
Интерфейс получает зашифрованныйopenID
а такжеunionID
Возможности данных не настраиваются. - новый
getUserProfile
Интерфейс (поддерживается с версии 2.10.4 базовой библиотеки) может получать информацию об аватаре, никнейме, поле и регионе пользователя.Каждый раз, когда разработчик получает личную информацию пользователя через этот интерфейс, пользователю необходимо ее подтвердить.
То есть разработчик вызывает через компонентwx.getUserInfo
Всплывающее окно больше не будет всплыть, а напрямую вернуть анонимную личную информацию пользователя. Если вы хотите получить пользовательский аватар, псевдоним, пол, региональная информация, необходимостьМодернизациясталиwx.getUserProfile
интерфейс.
2.3 Срок действия session_key
Если разработчик сталкиваетсяsession_key
Неправильная проверка подписи или сбой расшифровки, пожалуйста, обратите внимание на следующее иsession_key
соответствующие примечания.
-
wx.login
При вызове пользовательsession_key
может быть обновлен, чтобы отобразить старыйsession_key
Недействительно (механизм обновления имеет самый короткий цикл, если один и тот же пользователь звонит несколько раз за короткий промежуток времени)wx.login
, не каждый вызов приводит кsession_key
обновить). Разработчики должны звонить только тогда, когда им явно нужно повторно войти в систему.wx.login
, прошло во времениauth.code2Session
Сервер обновлений интерфейса сохраненsession_key
. - WeChat не будет
session_key
сообщить застройщику о сроке действия. Основываясь на поведении пользователя при использовании Мини-программы, мы будемsession_key
Продлить. Чем чаще пользователи используют мини-программы,session_key
Чем дольше срок действия. - разработчик в
session_key
В случае неудачи вы можете получить действительныйsession_key
. использовать интерфейсwx.checkSession
можно проверитьsession_key
Является ли он действительным, чтобы избежать многократного выполнения апплетом процесса входа в систему. - Когда разработчики реализуют настраиваемый статус входа в систему, они могут рассмотреть возможность использования
session_key
Срок действия используется в качестве срока действия собственного статуса входа в систему, также может быть реализована настраиваемая политика своевременности.
3 Архитектура входа
Архитектура решения «Вход», как показано выше, абстрагирует все функции, связанные с входом в систему."сервисный уровень"(Этот проект называет его какsession
),для«Бизнес-уровень»передача. В этой статье в основном описывается серый контент, а другие модули будут объяснены в следующей статье «Дизайн входа пользователя в мини-программу».
3.1 libs
- Обеспечьте методы класса, связанные с входом в систему, для вызова "бизнес-уровня"
- упаковка
session
Класс, предоставляющий методы класса для вызова «бизнес-уровня». В основном это следующие методы:
имя метода | Функция | сцены, которые будут использоваться |
---|---|---|
silentLogin |
Инициировать автоматический вход | - |
login |
Авторизоваться,silentLogin инкапсуляция метода |
Используется для инициации автоматического входа в систему при запуске апплета. |
refreshLogin |
обновить состояние входа,silentLogin инкапсуляция метода |
Используется для инициации автоматического входа в систему по истечении срока действия статуса входа. |
ensureSessionKey |
проверятьsessionKey Независимо от того, истек ли срок его действия, обновите статус входа в систему, когда он истечет. |
При привязке авторизованного номера мобильного телефона WeChat проверьте, не истек ли срок его действия.Если срок его действия истек, необходимо повторно авторизовать всплывающее окно. |
-
Декоратор:
-
fuse-line
: Механизм прерывателя цепи, если его вызвать несколько раз в течение короткого периода времени, перестанет отвечать на определенный период времени, аналогично медленному запуску TCP. для решенияrefreshLogin
,login
и другие методы параллельной обработки задач. -
single-queue
: в режиме с одной очередью одновременно допускается выполнение только одного сетевого запроса. После того, как запрос заблокирован, тот же запрос будет помещен в очередь, ожидая возврата текущего запроса, потребляя тот же результат. для решенияrefreshLogin
,login
и другие методы параллельной обработки задач.
-
4. Когда вызывать тихий вход
4.1 Вызывается при запуске апплета
Поскольку в большинстве случаев приходится полагаться на логистику, при запуске апплета (app.onLaunch()
) вызывает автоматический вход в систему, является наиболее распространенным средством. Здесь мы инкапсулируемlogin
Функция выглядит следующим образом, первый вызовwx.checkSession
судитьsession_key
Срок действия истек, еслиsession_key
Срок действия не истек и существует локальноauth_token
Определяемый пользователем статус входа означает, что текущий статус автоматического входа все еще действителен и никаких других операций не требуется. В противном случае это означает, что состояние автоматического входа в систему недопустимо или новый пользователь никогда не инициировал автоматический вход в систему, тогда инициируется процесс автоматического входа.
public async login(): Promise<void> {
// 调用wx.checkSession判断session_key是否过期
const hasSession = await checkSession();
// 本地已有可用登录态且session_key未过期,resolve。
if (this.getAuthToken() && hasSession) return Promise.resolve();
// 否则,发起静默登录
await this.silentLogin();
}
Однако из-за собственного процесса запуска апплета функции обработчиков жизненного цикла приложений, страниц и компонентов не поддерживаются.Асинхронная блокировка. Поэтому весьма вероятно, что после загрузки страницы апплета процесс автоматического входа в систему не был завершен, что приведет к некоторым последующим операциям, зависящим от состояния входа (например, инициация запроса).ошибка.
4.2 Вызывается при инициации запроса интерфейса
Чтобы быть в безопасности, если некоторым интерфейсам необходимо иметь настраиваемый статус входа в систему для аутентификации, им необходимо перехватить запрос, когда запрос инициирован, проверить статус входа и обновить имя входа. Код обновления для входа выглядит следующим образом:
public async refreshLogin(): Promise<void> {
try {
// 清除 Session
this.clearSession();
// 发起静默登录
await this.silentLogin();
} catch (error) {
throw error;
}
}
Весь процесс показан на рисунке ниже:
-
запрос на перехват:
-
Определить, требуется ли аутентификация: Когда запрос инициирован, перехватите запрос и определите, нужно ли добавить запрос.
auth-token
, если не требуется, инициируйте запрос напрямую. При необходимости перейдите к шагу 2. -
Определите, нужно ли вам инициировать автоматический вход в систему:судить
storage
существует вauth-token
, если он не существует, инициируйте «Обновить вход». -
добавить заголовок запроса
auth-token
:Добавить кauth-token
, инициируйте запрос.
-
Определить, требуется ли аутентификация: Когда запрос инициирован, перехватите запрос и определите, нужно ли добавить запрос.
- Связь с сервером: инициировать запрос, сервер обрабатывает запрос и возвращает результат.
-
перехватить ответ: Разобрать код состояния
-
Код состояния
AUTH_FAIL
: Сервер возвращаетсяcode
Есть две причины запуска этого сценария: во-первых, интерфейс должен быть аутентифицирован, но он не передается при инициации запроса.auth-token
, дваauth-token
Истекший. В это время последний запрос будет содержатьauth-token
с локальным хранилищемauth-token
Сравните, если оно несовместимо, это означает, что состояние входа в систему было обновлено, а затем повторно инициируйте запрос напрямую. Если последовательно,Инициировать обновление входа в систему, получить новыйauth-token
После повторной инициации запроса это действие незаметно для пользователя. -
Код состояния
USER_WX_SESSIONKEY_EXPIRE
: сервер возвращаетсяcode
Это «Статус входа в систему истек», который представляет собой код состояния, настроенный для авторизованного пользователя с ошибкой входа в систему по номеру мобильного телефона. Если срок действия статуса входа истек, это означает, что код состояния, хранящийся на сервереsession_key
Он также просрочен, тогда зашифрованные данные, полученные по клику авторизованного номера мобильного телефона, отправляются на сервер для симметричного расшифрования.session_key
Неверный, невозможно расшифровать настоящий номер мобильного телефона. Поэтому необходимо повторно инициировать автоматический вход в систему, дождаться, пока пользователь снова нажмет кнопку авторизации, чтобы получить новые зашифрованные данные, а затем инициировать новый запрос на расшифровку. -
Код состояния другой:Например
Success
Или, если другие бизнес-запросы неверны, перехват не выполняется, и возвращается ответ, позволяющий анализировать бизнес-код.
-
Код состояния
4.3 Загадка wx.checkSession Strike
Основываясь на процессе, вызываемом при инициировании вышеуказанного запроса интерфейса, у многих людей возникнут сомнения, так как сервер вернетauth-token
Истек срок действия кода состояния, почему бы не перехватить запрос перед отправкой, используйтеwx.checkSession
Проверяет ли интерфейс, не истек ли срок действия статуса входа (как показано на рисунке ниже, добавлены шаги в красном поле)?
Это потому, что мы обнаружили в ходе экспериментов, что вsession_key
истек,wx.checkSession
Есть определенный шанс вернутьсяtrue
. Увеличениеwx.checkSession
Шаги не гарантируют на 100%, что срок действия статуса входа не истечет, и в будущем все равно необходимо будет обрабатывать различные коды статуса.
Есть также соответствующие отзывы от сообщества, которые не были учтены:
- Апплет расшифровывает номер мобильного телефона, через небольшой промежуток времени чексессия: все в порядке, но расшифровка не удалась
- wx.checkSession работает, но расшифровать данные не удается
- checkSession считает, что session_key не является недействительным, но расшифровка номера мобильного телефона не удалась
Итак, вывод такой:wx.checkSession
Надежность менее 100%.
На основании вышеизложенного нам необходимоsession_key
Срок действия делает некоторую отказоустойчивость:
- Инициировать потребность в использовании
session_key
перед запросом сделать это один разwx.checkSession
Операция в случае сбоя обновляет состояние входа. - использование серверной части
session_key
После сбоя расшифровки открытых данных возвращается определенный код ошибки (например:USER_WX_SESSIONKEY_EXPIRE
), внешний интерфейс обновляет состояние входа.
4.4 Параллельная обработка
Мы знаем, что при запуске апплета различные средства мониторинга и отчетности должны получать личную информацию пользователя, которую можно получить только после «тихого входа в систему», поэтому несколькоlogin
просить. В другом случае предположим, что новый пользователь заходит на сложную бизнес-страницу и одновременно инициирует пять разных бизнес-запросов.Бывает, что все эти пять запросов требуют аутентификации, тогда все пять запросов будут перехвачены и инициированы.refreshLogin
просить. Очевидно, что такой параллелизм неразумен.
Исходя из этого, мы разработали следующую схему:
-
режим одиночной очереди:
-
запросить блокировку: Одновременно разрешен только один выполняющийся сетевой запрос.
-
очередь ожидания: после того, как запрос заблокирован, тот же запрос будет помещен в очередь, и тот же результат будет использован после ожидания возврата текущего запроса.
-
-
автоматический выключатель: при многократном вызове в течение короткого промежутка времени перестает отвечать на запросы в течение определенного периода времени, аналогично медленному запуску TCP.
Как показано на рисунке выше, сначалаrefreshLogin
Запрос поставлен в очередь, и в очереди есть только один запрос.Отправьте запрос, и предохранитель считается равным 1. Сервер возвращает результат запроса и использует результат. Затем запустите другойrefreshLogin
запрос, в очереди только один запрос, отправьте запрос, а фьюз засчитывается как 2. Затем инициируются три последовательных запроса. Поскольку предыдущий запрос не был выполнен, три запроса помещаются в очередь и возвращается результат предыдущего запроса. Четыре запроса в очереди потребляют один и тот же результат. Предохранитель сбрасывается из-за срабатывания порога автоматического охлаждения.
Вышеуказанные две схемы вводятся через режим декоратора, код такой:refreshLogin
Функция на самом делеslientLogin
Слой инкапсуляции функций, который вызывается при инициализации интерфейса. в то время как вышеупомянутыйlogin
функция такжеslientLogin
Уровень инкапсуляции функции, которая вызывается при запуске пользовательского апплета.
@singleQueue({ name: 'refreshLogin' })
@fuseLine({ name: 'refreshLogin' })
public async refreshLogin(): Promise<void> {
try {
// 清除 Session
this.clearSession();
await this.silentLogin();
} catch (error) {
throw error;
}
}
На данный момент многие читатели могут еще не понять механизм предохранителя. Назначение предохранителя состоит в том, чтобы обеспечить защиту предохранителя для функции. все запросы будут отклонены в течение этого времени. Если ни один запрос не проходит в пределах порога автоматического охлаждения, сбросьте предохранитель. Код выглядит следующим образом:
export default function fuseLine({
// 一次熔断前重试次数
tryTimes = 3,
// 重试间隔,单位 ms
restoreTime = 5000,
// 自动冷却阈值,单位 ms
coolDownThreshold = 1000,
// 名称
name = 'unnamed',
}: {
tryTimes?: number;
restoreTime?: number;
name?: string;
coolDownThreshold?: number;
} = {}) {
// 请求锁
let fuseLocked = false;
// 当前重试次数
let fuseTryTimes = tryTimes;
// 自动冷却
let coolDownTimer;
// 重置保险丝
const reset = () => {
fuseLocked = false;
fuseTryTimes = tryTimes;
logger.info(`${name}-保险丝重置`);
};
const request = async () => {
if (fuseLocked) throw new Error(`${name}-保险丝已熔断,请稍后重试`);
// 已达最大重试次数
if (fuseTryTimes <= 0) {
fuseLocked = true;
// 重置保险丝
setTimeout(() => reset(), restoreTime);
throw new Error(`${name}-保险丝熔断!!`);
}
// 自动冷却系统
if (coolDownTimer) clearTimeout(coolDownTimer);
coolDownTimer = setTimeout(() => reset(), coolDownThreshold);
// 允许当前请求通过保险丝,记录 +1
fuseTryTimes = fuseTryTimes - 1;
logger.info(`${name}-通过保险丝(${tryTimes - fuseTryTimes}/${tryTimes})`);
return Promise.resolve();
};
return function(
_target: Record<string, any>,
_propertyName: string,
descriptor: TypedPropertyDescriptor<(...args: any[]) => any>,
) {
const method = descriptor.value;
descriptor.value = async function(...args: any[]) {
await request();
if (method) return method.apply(this, args);
};
};
}
5. Наконец
Прочитав это, я полагаю, вы уже поняли разницу между «автоматическим входом в систему» и «пользовательским входом в систему». «Тихий вход» — это процесс получения статуса входа в WeChat.Получив идентификатор пользователя, предоставленный WeChat, можно быстро установить пользовательскую систему в апплете. «Вход пользователя» — это процесс, в котором пользователь разрешает физическому лицу открывать данные, чтобы стать участником. Это относится к преобразованию из гостевого состояния в состояние участника с полномочиями на покупку и другие операции.
Это не одно и то же понятие, «логин пользователя» будет обсуждаться в следующей статье.«Дизайн архитектуры входа пользователя в мини-программу»объяснил в.