Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

coldplay.xixi
Libérer: 2020-10-28 17:24:42
avant
3612 Les gens l'ont consulté

La rubrique Développement du mini programme WeChat d'aujourd'hui présente la conception frontale et la mise en œuvre de la connexion au mini programme.

1. Avant-propos

La raison pour laquelle la conception de la connexion/inscription est si soigneusement conçue est bien sûr de rendre cette fonctionnalité de base de l'application suffisamment robuste pour éviter l'apparition de l'ensemble. site. obstruction sexuelle.

Dans le même temps, nous devons réfléchir pleinement à la manière de découpler et d'encapsuler lors du développement de nouveaux petits programmes, nous pouvons rapidement supprimer les capacités de réutilisation et éviter les pièges répétés.

Le module de connexion et d'inscription est comme un iceberg. Nous pensons qu'il suffit de "entrer votre compte et votre mot de passe, et vous êtes connecté". Mais en fait, il y a plusieurs problèmes à prendre en compte.

Ici, j'aimerais partager avec vous toutes les expériences et idées de conception que j'ai accumulées après avoir récemment terminé un petit module de connexion/inscription au programme.

2. Scénarios commerciaux

Dans le processus de navigation des utilisateurs dans le mini-programme, en raison des besoins professionnels, il est souvent nécessaire d'obtenir certaines informations de base sur l'utilisateur :

    Pseudo WeChat
  1. Numéro de téléphone WeChat
Différents produits ont des exigences d'information différentes pour les utilisateurs et ont des processus d'autorisation différents.

Le premier type est courant dans les systèmes de commerce électronique. Lorsque les utilisateurs achètent des biens, afin d'identifier le compte multiplateforme de l'utilisateur, ils utilisent souvent leur numéro de téléphone mobile pour établir un contact. l'utilisateur doit autoriser le numéro de téléphone mobile.

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

Deuxièmement, afin d'obtenir une initialisation de base des informations utilisateur, il est souvent nécessaire d'obtenir davantage d'informations utilisateur : telles que le pseudo WeChat,

, etc., vous besoin de demander l'autorisation de l'utilisateur. unionId

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

Le troisième type comprend le premier type et le deuxième type.

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

3. Concept

Dans le but de précipiter un ensemble de solutions et de services de connexion universels à des mini-programmes, analysons l'activité et obtenons les variables.

Avant de faire de la conception technique, dites quelques bêtises nécessaires et effectuez des réglages de base sur certains concepts.

2.1 À propos de « Login »

Login est « login » en anglais, et celui correspondant est « logout ». Avant de vous connecter, vous devez avoir un compte et vous « inscrire » (ou vous inscrire).

Au début, le produit n'avait pas de fonction de connexion/enregistrement, mais il est progressivement devenu disponible au fur et à mesure que de plus en plus de personnes l'utilisaient. En raison des besoins du produit lui-même, « l’utilisateur » doit être identifié.

Dans la société réelle, chacun de nous possède une pièce d'identité : la carte d'identité. Quand j'ai eu 16 ans, j'ai effectué une « inscription » lorsque je suis allé au Bureau de la sécurité publique pour obtenir ma carte d'identité pour la première fois. Ensuite, je suis allé au cybercafé pour surfer sur Internet, j'ai glissé ma carte d'identité et j'ai effectué un comportement de « connexion ».

Donc pour Internet dans le monde virtuel, ce certificat d'identité est "compte + mot de passe".

Les méthodes de connexion/d'enregistrement courantes sont :

  1. Enregistrement d'un compte et d'un mot de passe

    Au début d'Internet, les informations personnelles La couverture des e-mails et des téléphones portables est limitée. Par conséquent, l'utilisateur doit penser à un nom de compte. Nous enregistrons un compte QQ, qui est ce formulaire.

    from 汽车之家

  2. Enregistrement d'une adresse e-mail

    Après le millénaire, l'ère Internet des PC est rapidement devenue populaire , et nous avons tous créé Obtenez votre propre e-mail personnel. De plus, QQ est également livré avec un compte de messagerie. Étant donné que l'e-mail est privé et peut communiquer des informations, la plupart des sites Web ont commencé à utiliser des comptes de messagerie comme noms d'utilisateur pour l'enregistrement, et devront se connecter à l'e-mail correspondant pour vérifier les e-mails d'activation pendant le processus d'inscription afin de vérifier que nous en sommes propriétaires. l'adresse e-mail enregistrée.

    from 支付宝

  3. Enregistrement du numéro de téléphone portable

    Après la popularité d'Internet, les smartphones et l'Internet mobile se sont développés rapidement . Les téléphones mobiles sont devenus un appareil mobile indispensable pour chacun, et l'Internet mobile est profondément intégré dans la vie moderne de chacun. Par conséquent, par rapport au courrier électronique, les numéros de téléphone mobile sont actuellement plus étroitement liés aux individus, et de plus en plus d'applications mobiles apparaissent, et les méthodes d'enregistrement qui utilisent les numéros de téléphone mobile comme noms d'utilisateur sont également largement utilisées.

    from 知乎

En 2020, le nombre d'utilisateurs de WeChat a atteint 1,2 milliard. Eh bien, les comptes WeChat, du moins en Chine, sont devenus la « marque d'identité » de la nouvelle génération du monde Internet.

Pour l'applet WeChat, il est naturel de connaître l'ID de compte WeChat de l'utilisateur actuel. WeChat permet aux applications de mini-programmes de se « connecter » silencieusement à nos applications de mini-programmes sans que l'utilisateur s'en rende compte. C'est ce que nous appelons souvent « connexion silencieuse ».

En fait, la connexion de l'applet WeChat est essentiellement le même concept que « l'authentification unique » des applications Web traditionnelles.

  1. Authentification unique : après s'être connectés au site A, les sites C et B peuvent obtenir une "connexion silencieuse" rapide.
  2. Connexion au mini-programme WeChat : Dans WeChat, si vous vous connectez à votre compte WeChat, vous pouvez obtenir une « connexion silencieuse » dans l'ensemble de l'écosystème du mini-programme.

Étant donné que HTTP est à l'origine sans état, l'approche générale de base de l'industrie en matière de statut de connexion est la suivante :

  1. session de cookie : couramment utilisée dans les applications de navigateur
  2. Accès jeton : Couramment utilisé dans les applications non-navigateurs telles que les terminaux mobiles

Dans l'applet WeChat, la "couche logique JS" n'est pas un environnement de navigateur, donc naturellement il n'y en a pasCookie, donc généralement Utilisera la méthode access token.

2.2 À propos de « Autorisation »

Pour les produits qui doivent obtenir davantage de surnoms d'utilisateurs, de numéros de téléphone portable d'utilisateurs et d'autres informations. Dans un souci de confidentialité des utilisateurs, WeChat exige que les utilisateurs acceptent activement d'autoriser. Seules les applications de mini-programmes peuvent obtenir cette partie des informations. Il s'agit de l'interaction des mini-programmes actuellement populaires « informations sur l'utilisateur autorisé » et « numéro de téléphone mobile autorisé ».

En raison des différentes sensibilités des différentes informations utilisateur, les mini-programmes WeChat fournissent une « autorisation » pour différentes informations utilisateur de différentes manières :

  1. Appelez la méthode API spécifique, autorisation contextuelle .
    1. Par exemple, lors de l'appel de wx.getLocation(), si l'utilisateur n'est pas autorisé, l'interface d'autorisation d'adresse apparaîtra.
    2. En cas de rejet, la fenêtre ne réapparaîtra pas et wx.getLocation()reviendra directement à l'échec.
  2. <button open-type="xxx"></button> manière.
    1. Prend uniquement en charge : les informations sensibles de l'utilisateur et le numéro de téléphone mobile de l'utilisateur. Vous devez coopérer avec le backend pour un cryptage et un décryptage symétriques afin d'obtenir les données.
    2. L'utilisateur a refusé. Si vous cliquez à nouveau sur le bouton, la fenêtre contextuelle apparaîtra toujours.
  3. Passez wx.authorize() pour demander une autorisation à l'avance, et vous n'avez pas besoin de réafficher l'autorisation lorsque vous avez besoin d'obtenir des informations pertinentes plus tard.

4. Conception détaillée

Après avoir trié le concept, notre module peut être divisé en deux parties principales :

  1. Connexion : Responsable de la création d'une session avec le serveur. Cette session implémente la connexion silencieuse et le traitement de tolérance aux pannes associé. Le module est nommé : Session
  2. Autorisation : Responsable de la communication avec le. serveur. Interaction de l'utilisateur, obtention et mise à jour des informations et traitement du contrôle des autorisations, etc. Le module est nommé : Auth

3.1 Mise en œuvre de la connexion

3.1.1 Connexion silencieuse

3.1.1 Connexion silencieuseComprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

    La solution de connexion officielle fournie par WeChat est résumée en trois étapes :
  1. wx.login()
  2. Le front-end obtient le code d'identification crypté unique via
  3. et le remet au back-end. openIdsession_keyLe backend transmet ce code au serveur WeChat en échange de l'identifiant unique de l'utilisateur
  4. et du certificat d'autorisation
  5. . (Appels d'API spéciaux utilisés pour les serveurs ultérieurs côté serveur et WeChat, veuillez consulter : Documentation officielle de WeChat - accès côté serveur aux données ouvertes).
Le backend transmet les informations d'identification de l'utilisateur obtenues à partir du serveur WeChat et les informations de connexion auto-générées (jeton) au frontend. Le front-end l'enregistre et l'amène au back-end la prochaine fois qu'il le demande, afin qu'il puisse identifier quel utilisateur.

Si vous mettez simplement en œuvre ce processus, c'est assez simple.

    Mais pour mettre en œuvre un processus de connexion robuste, vous devez faire attention à des cas plus extrêmes :
  1. wx.login()Réduire

    appeler

     :wx.login()session_keyParce que session.login() produira des effets secondaires imprévisibles, par exemple, cela peut rendre wx.login() invalide, entraînant l'échec des scénarios de décryptage d'autorisation ultérieurs. Ici, nous pouvons fournir une méthode comme

    pour prendre le contrôle de
  2. et y effectuer une série de traitements d'encapsulation et de tolérance aux pannes.
  3. Moment de l'appel

     : app.onLaunch()

    Habituellement, nous lançons une connexion silencieuse au démarrage de l'application (). Cependant, il existe un problème asynchrone causé par le problème de conception du cycle de vie de l'applet : lors du chargement de la page, lors de l'appel d'une API back-end qui nécessite un état de connexion, le processus de connexion statique asynchrone précédent peut ne pas être terminé, ce qui entraîne un échec de la demande.

    Bien sûr, vous pouvez également lancer un appel de connexion de manière bloquante asynchrone lorsque la première interface nécessitant un statut de connexion est appelée. Cela nécessite une couche d'interface bien conçue.

    Les idées de conception détaillées pour les deux scénarios mentionnés ci-dessus seront discutées ci-dessous.

  4. Problèmes avec les appels simultanés :

    Dans les scénarios professionnels, il est inévitable que plusieurs codes doivent déclencher la connexion si vous. face à des situations extrêmes, ces multiples codes lancent des appels en même temps. Cela entraînera le lancement du processus de connexion plusieurs fois dans un court laps de temps, même si la demande précédente n'a pas été complétée. Dans cette situation, nous pouvons bloquer le premier appel et attendre le résultat des appels suivants, tout comme le processus d’union des spermatozoïdes et des ovules.

  5. Problème avec les appels non expirés :

    Si notre statut de connexion n'a pas expiré, il peut être utilisé normalement par défaut, ce n'est pas nécessaire. pour rappeler. Allez lancer le processus de connexion. À ce stade, nous pouvons d'abord vérifier si l'état de connexion est disponible par défaut. Sinon, nous pouvons lancer une demande. Ensuite, vous pouvez également fournir un paramètre similaire à session.login({ force: true }) pour forcer la connexion.

3.1.2 Gestion de l'état asynchrone de connexion silencieuse

1 Appelé au démarrage de l'application

Car dans la plupart des cas. Ils doivent tous s'appuyer sur l'état de connexion. On pensera naturellement à appeler cet appel au démarrage de l'application (app.onLaunch()).

Cependant, en raison du processus de démarrage natif de l'applet, les fonctions de hook de cycle de vie de App, Page et Component ne prennent pas en charge le blocage asynchrone.

Ensuite, nous pouvons facilement constater que le "processus de connexion" initié par app.onLaunch n'est pas terminé au moment de page.onLoad, et nous ne pourrons pas effectuer correctement certaines opérations qui dépendent de la connexion État.

Pour cette situation, nous avons conçu une machine-outil d'état : status

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

Basé sur la machine d'état, nous pouvons écrire du code comme celui-ci :

import { Status } from '@beautywe/plugin-status';// on app.jsApp({    status: {       login: new Status('login');
    },    onLaunch() {
        session            // 发起静默登录调用
            .login()            // 把Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat设置为 success
            .then(() => this.status.login.success())      
            // 把Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat设置为 fail
            .catch(() => this.status.login.fail());
    },
});// on page.jsPage({    onLoad() {      const loginStatus = getApp().status.login;      
      // must 里面会进行状态的判断,例如登录中就等待,登录成功就直接返回,登录失败抛出等。
      loginStatus().status.login.must(() => {        // 进行一些需要登录态的操作...
      });
    },
});复制代码
Copier après la connexion

2. Initiez la connexion lorsque la "première interface nécessitant un état de connexion" est appelée

De plus, nous constaterons qu'un niveau plus profond d'état de connexion est requis. Le nœud est lorsque le " L'API backend qui nécessite un statut de connexion" est lancée.

Ensuite, nous pouvons lancer une "connexion silencieuse" lors de l'appel d'une "API backend qui nécessite un statut de connexion". Pour les scénarios simultanés, laissez simplement les autres requêtes attendre.

En utilisant fly.js comme "couche de requête réseau" encapsulée par wx.request(), faites un exemple simple :

// 发起请求,并表明该请求是需要登录态的fly.post('https://...', params, { needLogin: true });// 在 fly 拦截器中处理逻辑fly.interceptors.request.use(async (req)=>{  // 在请求需要登录态的时候
  if (req.needLogin !== false) {    // ensureLogin 核心逻辑是:判断是否已登录,如否发起登录调用,如果正在登录,则进入队列等待回调。
    await session.ensureLogin();    
    // 登录成功后,获取 token,通过 headers 传递给后端。
    const token = await session.getToken();    Object.assign(req.headers, { [AUTH_KEY_NAME]: token });
  }  
  return req;
});复制代码
Copier après la connexion

3.1.3 Traitement tolérant aux pannes de l'expiration de l'état de connexion personnalisé

Lorsque le statut de connexion personnalisé expire, le backend doit renvoyer un code de statut spécifique, tel que : AUTH_EXPIRED, AUTH_INVALID, etc.

Le front-end peut surveiller le code d'état de toutes les requêtes dans la "couche de requêtes réseau", puis lancer une actualisation de l'état de connexion, puis rejouer la requête ayant échoué :

// 添加响应拦截器fly.interceptors.response.use(    (response) => {      const code = res.data;        
      // 登录态过期或失效
      if ( ['AUTH_EXPIRED', 'AUTH_INVALID'].includes(code) ) {      
        // 刷新登录态
        await session.refreshLogin();        
        // 然后重新发起请求
        return fly.request(request);
      }
    }
)复制代码
Copier après la connexion

Ensuite, si il existe de nombreuses requêtes simultanées. Chaque requête renvoie un code d'état indiquant que l'état de connexion n'est pas valide, et le code ci-dessus sera exécuté plusieurs fois.

Nous devons effectuer un traitement spécial de tolérance aux pannes pour session.refreshLogin() :

  1. Verrouillage de la demande : une seule demande réseau en cours est autorisée à la fois. en même temps.
  2. File d'attente : Une fois la requête verrouillée, tous les appels à cette méthode sont placés dans une file d'attente, en attendant que la requête réseau soit terminée pour partager le résultat du retour.
  3. Mécanisme de disjoncteur : s'il est appelé plusieurs fois sur une courte période de temps, il cessera de répondre pendant un certain temps, similaire au démarrage lent de TCP.

Exemple de code :

class Session {  // ....
  
  // 刷新登录保险丝,最多重复 3 次,然后熔断,5s 后恢复
  refreshLoginFuseLine = REFRESH_LOGIN_FUSELINE_DEFAULT;
  refreshLoginFuseLocked = false;
  refreshLoginFuseRestoreTime = 5000;  // 熔断控制
  refreshLoginFuse(): Promise<void> {    if (this.refreshLoginFuseLocked) {      return Promise.reject('刷新登录-保险丝已熔断,请稍后');
    }    if (this.refreshLoginFuseLine > 0) {      this.refreshLoginFuseLine = this.refreshLoginFuseLine - 1;      return Promise.resolve();
    } else {      this.refreshLoginFuseLocked = true;      setTimeout(() => {        this.refreshLoginFuseLocked = false;        this.refreshLoginFuseLine = REFRESH_LOGIN_FUSELINE_DEFAULT;
        logger.info('刷新登录-保险丝熔断解除');
      }, this.refreshLoginFuseRestoreTime);      return Promise.reject('刷新登录-保险丝熔断!!');
    }
  }  // 并发回调队列
  refreshLoginQueueMaxLength = 100;
  refreshLoginQueue: any[] = [];
  refreshLoginLocked = false;  // 刷新登录态
  refreshLogin(): Promise<void> {    return Promise.resolve()    
      // 回调队列 + 熔断 控制
      .then(() => this.refreshLoginFuse())
      .then(() => {        if (this.refreshLoginLocked) {          const maxLength = this.refreshLoginQueueMaxLength;          if (this.refreshLoginQueue.length >= maxLength) {            return Promise.reject(`refreshLoginQueue 超出容量:${maxLength}`);
          }          return new Promise((resolve, reject) => {            this.refreshLoginQueue.push([resolve, reject]);
          });
        }        this.refreshLoginLocked = true;
      })      // 通过前置控制之后,发起登录过程
      .then(() => {        this.clearSession();
        wx.showLoading({ title: '刷新登录态中', mask: true });        return this.login()
          .then(() => {
            wx.hideLoading();
            wx.showToast({ icon: 'none', title: '登录成功' });            this.refreshLoginQueue.forEach(([resolve]) => resolve());            this.refreshLoginLocked = false;
          })
          .catch(err => {
            wx.hideLoading();
            wx.showToast({ icon: 'none', title: '登录失败' });            this.refreshLoginQueue.forEach(([, reject]) => reject());            this.refreshLoginLocked = false;            throw err;
          });
      });  // ...}复制代码</void></void>
Copier après la connexion

3.1.4 Traitement tolérant aux pannes de l'expiration de la clé de session WeChat

Après avoir effectué la « connexion silencieuse » ci-dessus, le serveur WeChat le problème A session_key est destiné au backend, et il sera utilisé lorsque vous aurez besoin d'obtenir des données ouvertes WeChat.

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

Et session_key est sensible au temps. Ce qui suit est tiré de la description officielle de WeChat :

Validité de la clé de session session_key

Si les développeurs rencontrent un échec de vérification de signature ou un échec de déchiffrement en raison d'une session_key incorrecte, veuillez prêter attention aux notes suivantes liées à session_key.

  1. wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储的 session_key。
  2. 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
  3. 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
  4. 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。

翻译成简单的两句话:

  1. session_key 时效性由微信控制,开发者不可预测。
  2. wx.login 可能会导致 session_key 过期,可以在使用接口之前用 wx.checkSession 检查一下。

而对于第二点,我们通过实验发现,偶发性的在 session_key 已过期的情况下,wx.checkSession 会概率性返回 true

社区也有相关的反馈未得到解决:

  • 小程序解密手机号,隔一小段时间后,checksession:ok,但是解密失败
  • wx.checkSession有效,但是解密数据失败
  • checkSession判断session_key未失效,但是解密手机号失败

所以结论是:wx.checkSession可靠性是不达 100% 的。

基于以上,我们需要对 session_key 的过期做一些容错处理:

  1. 发起需要使用 session_key 的请求前,做一次 wx.checkSession 操作,如果失败了刷新登录态。
  2. 后端使用 session_key 解密开放数据失败之后,返回特定错误码(如:DECRYPT_WX_OPEN_DATA_FAIL),前端刷新登录态。

示例代码:

// 定义检查 session_key 有效性的操作const ensureSessionKey = async () => {  const hasSession = await new Promise(resolve => {
    wx.checkSession({      success: () => resolve(true),      fail: () => resolve(false),
    });
  });  
  if (!hasSession) {
    logger.info('sessionKey 已过期,刷新登录态');    // 接上面提到的刷新登录逻辑
    return session.refreshLogin();
  }  return Promise.resolve();
}// 在发起请求的时候,先做一次确保 session_key 最新的操作(以 fly.js 作为网络请求层为例)const updatePhone = async (params) => {  await ensureSessionKey();  const res = await fly.post('https://xxx', params);
}// 添加响应拦截器, 监听网络请求返回fly.interceptors.response.use(    (response) => {      const code = res.data;        
      // 登录态过期或失效
      if ( ['DECRYPT_WX_OPEN_DATA_FAIL'].includes(code)) {        // 刷新登录态
        await session.refreshLogin();        
        // 由于加密场景的加密数据由用户点击产生,session_key 可能已经更改,需要用户重新点击一遍。
        wx.showToast({ title: '网络出小差了,请稍后重试', icon: 'none' });
      }
    }
)复制代码
Copier après la connexion

3.2 授权的实现

3.2.1 组件拆分与设计

在用户信息和手机号获取的方式上,微信是以 <button open-type="'xxx'"></button> 的方式,让用户主动点击授权的。

那么为了让代码更解耦,我们设计这样三个组件:

  1. <user-contaienr getuserinfo="onUserInfoAuth"></user-contaienr>: 包装点击交互,通过 <slot></slot> 支持点击区域的自定义UI。
  2. <phone-container getphonennmber="onPhoneAuth"></phone-container> : 与 <user-container></user-container> 同理。
  3. <auth-flow></auth-flow>: 根据业务需要,组合 <user-container></user-container><phone-container></phone-container> 组合来定义不同的授权流程。

以开头的业务场景的流程为例,它有这样的要求:

  1. 有多个步骤。
  2. 如果中途断掉了,可以从中间接上。
  3. 有些场景中,只要求达到「用户信息授权」,而不需要完成「用户手机号」。

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

那么授权的阶段可以分三层:

// 用户登录的阶段export enum AuthStep {  // 阶段一:只有登录态,没有用户信息,没有手机号
  ONE = 1,  // 阶段二:有用户信息,没有手机号
  TWO = 2,  // 阶段三:有用户信息,有手机号
  THREE = 3,
}复制代码
Copier après la connexion

AuthStep 的推进过程是不可逆的,我们可以定义一个 nextStep 函数来封装 AuthStep 更新的逻辑。外部使用的话,只要无脑调用 nextStep 方法,等待回调结果就行。

示例伪代码:

// auth-flow componentComponent({  // ...
  
  data: {    // 默认情况下,只需要到达阶段二。
    mustAuthStep: AuthStep.TWO
  },  
  // 允许临时更改组件的需要达到的阶段。
  setMustAuthStep(mustAuthStep: AuthStep) {    this.setData({ mustAuthStep });
  },  
  // 根据用户当前的信息,计算用户处在授权的阶段
  getAuthStep() {    let currAuthStep;    
    // 没有用户信息,尚在第一步
    if (!session.hasUser() || !session.hasUnionId()) {
      currAuthStep = AuthStepType.ONE;
    }    // 没有手机号,尚在第二步
    if (!session.hasPhone()) {
      currAuthStep = AuthStepType.TWO;
    }    // 都有,尚在第三步
    currAuthStep = AuthStepType.THREE;    return currAuthStep;
  }  
  // 发起下一步授权,如果都已经完成,就直接返回成功。
  nextStep(e) {    const { mustAuthStep } = this.data;    const currAuthStep = this.updateAuthStep();  
    // 已完成授权
    if (currAuthStep >= mustAuthStep || currAuthStep === AuthStepType.THREE) {      // 更新全局的授权Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat,广播消息给订阅者。
      return getApp().status.auth.success();
    }    // 第一步:更新用户信息
    if (currAuthStep === AuthStepType.ONE) {      // 已有密文信息,更新用户信息
      if (e) session.updateUser(e);      // 更新到视图层,展示对应UI,等待获取用户信息
      else this.setData({ currAuthStep });      return;
    }    // 第二步:更新手机信息
    if (currAuthStep === AuthStepType.TWO) {      // 已有密文信息,更新手机号
      if (e) this.bindPhone(e);      // 未有密文信息,弹出获取窗口
      else this.setData({ currAuthStep });      return;
    }    console.warn('auth.nextStep 错误', { currAuthStep, mustAuthStep });
  },  
  // ...});复制代码
Copier après la connexion

那么我们的 <auth-flow></auth-flow> 中就可以根据 currAuthStepmustAuthStep 来去做不同的 UI 展示。需要注意的是使用 <user-container></user-container><phone-container></phone-container> 的时候连接上 nextStep(e) 函数。

示例伪代码:

<view>

  <!-- 已完成授权 -->
  <block>
    <view>已完成授权</view>
  </block>

  <!-- 未完成授权,第一步:Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat -->
  <block>
    <user-container>
      <view>Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat</view>
    </user-container>
  </block>

  <!-- 未完成授权,第二步:Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat -->
  <block>
    <phone-container>
      <view>Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat</view>
    </phone-container>
  </block>
  </view>复制代码
Copier après la connexion

3.2.2 权限拦截的处理

到这里,我们制作好了用来承载授权流程的组件 <auth-flow></auth-flow> ,那么接下来就是决定要使用它的时机了。

我们梳理需要授权的场景:

  1. 点击某个按钮,例如:购买某个商品。

    对于这种场景,常见的是通过弹窗完成授权,用户可以选择关闭。

    Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

  2. 浏览某个页面,例如:访问个人中心。

    对于这种场景,我们可以在点击跳转某个页面的时候,进行拦截,弹窗处理。但这样的缺点是,跳转到目标页面的地方可能会很多,每个都拦截,难免会错漏。而且当目标页面作为「小程序落地页面」的时候,就避免不了。

    这时候,我们可以通过重定向到授权页面来完成授权流程,完成之后,再回来。

    Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

那么我们定义一个枚举变量:

// 授权的展示形式export enum AuthDisplayMode {  // 以弹窗形式
  POPUP = 'button',  // 以页面形式
  PAGE = 'page',
}复制代码
Copier après la connexion

我们可以设计一个 mustAuth 方法,在点击某个按钮,或者页面加载的时候,进行授权控制。

伪代码示例:

class Session {  // ...
  
  mustAuth({
    mustAuthStep = AuthStepType.TWO, // 需要授权的LEVEL,默认需要获取用户资料
    popupCompName = 'auth-popup',	// 授权弹窗组件的 id
    mode = AuthDisplayMode.POPUP, // 默认以弹窗模式
  } = {}): Promise<void> {    
    // 如果当前的授权步骤已经达标,则返回成功
    if (this.currentAuthStep() >= mustAuthStep) return Promise.resolve();    // 尝试获取当前页面的 <auth-popup></auth-popup> 组件实例
    const pages = getCurrentPages();    const curPage = pages[pages.length - 1];    const popupComp = curPage.selectComponent(`#${popupCompName}`);    // 组件不存在或者显示指定页面,跳转到授权页面
    if (!popupComp || mode === AuthDisplayMode.PAGE) {      const curRoute = curPage.route;      // 跳转到授权页面,带上当前页面路由,授权完成之后,回到当前页面。
      wx.redirectTo({ url: `authPage?backTo=${encodeURIComponent(curRoute)}` });      return Promise.resolve();
    }    
    // 设置授权 LEVEL,然后调用 <auth-popup> 的 nextStep 方法,进行进一步的授权。
    popupComp.setMustAuthStep(mustAuthStep);
    popupComp.nextStep();    // 等待成功回调或者失败回调
    return new Promise((resolve, reject) => {      const authStatus = getApp().status.auth;
      authStatus.onceSuccess(resolve);
      authStatus.onceFail(reject);
    });
  }  
  // ...}复制代码</auth-popup></void>
Copier après la connexion

那么我们就能在按钮点击,或者页面加载的时候进行授权拦截:

Page({  onLoad() {
    session.mustAuth().then(() => {      // 开始初始化页面...
    });
  }  
  onClick(e) {
    session.mustAuth().then(() => {      // 开始处理回调逻辑...
    });
  }
})复制代码
Copier après la connexion

当然,如果项目使用了 TS 的话,或者支持 ES7 Decorator 特性的话,我们可以为 mustAuth 提供一个装饰器版本:

export function mustAuth(option = {}) {  return function(
    _target,
    _propertyName,
    descriptor,  ) {    // 劫持目标方法
    const method = descriptor.value;    
    // 重写目标方法
    descriptor.value = function(...args: any[]) {      return session.mustAuth(option).then(() => {        // 登录完成之后,重放原来方法
        if (method) return method.apply(this, args);
      });
    };
  };
}复制代码
Copier après la connexion

那么使用方式就简单一些了:

Page({
  @mustAuth();  onLoad() {    // 开始初始化页面...
  }
  
  @mustAuth();  onClick(e) {    // 开始处理回调逻辑...
  }
});复制代码
Copier après la connexion

3.3. 前后端交互协议整理

作为一套可复用的小程序登录方案,当然需要去定义好前后端的交互协议。

那么整套登录流程下来,需要的接口有这么几个:

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

  1. 静默登录 silentLogin

    1. 入参:
      1. code: 产自 wx.login()
    2. 出参:
      1. token: 自定义登录态凭证
      2. userInfo: 用户信息
    3. 说明:
      1. 后端利用 code 跟微信客户端换取用户标识,然后注册并登录用户,返回自定义登录态 token 给前端
      2. token 前端会存起来,每个请求都会带上
      3. userInfo 需要包含nicknamephone字段,前端用于计算当前用户的授权阶段。当然这个状态的记录可以放在后端,但是我们认为放在前端,会更加灵活。
  2. 更新用户信息 updateUser

    1. 入参:
      1. nickname: 用户昵称
      2. encrypt: Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat相关的 iv, encryptedData
      3. 以及其他如性别地址等非必要字段
    2. 出参:
      1. userInfo:更新后的最新用户信息
    3. 说明:
      1. 后端解密Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat,获取隐蔽数据,如:unionId
      2. 后端支持更新包括 nickname等用户基本信息。
      3. 前端会把 userInfo 信息更新到 session 中,用于计算授权阶段。
  3. 更新用户手机号 updatePhone

    1. 入参:
      1. encrypt:Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat相关的 iv, encryptedData
    2. 出参:
      1. userInfo:更新后的最新用户信息
    3. 说明:
      1. 后端解密开放式局,获取手机号,并更新到用户信息中。
      2. 前端会把 userInfo 信息更新到 session 中,用于计算授权阶段。
  4. 解绑手机号 unbindPhone

    1. 入参:-
    2. 出参:-
    3. 说明:后端解绑用户手机号,成功与否,走业务定义的前后端协议。
  5. 登录 logout

    1. 入参:-

    2. 出参:-

    3. 说明:后端主动过期登录态,成功与否,走业务定义的前后端协议。

五. 架构图

最后我们来梳理一下整体的「登录服务」的架构图:

Comprendre la conception frontale et la mise en œuvre de la connexion au mini-programme WeChat

由「登录服务」和「底层建设」组合提供的通用服务,业务层只需要去根据产品需求,定制授权的流程 <auth-flow></auth-flow> ,就能满足大部分场景了。

六. 总结

本篇文章通过一些常见的登录授权场景来展开来描述细节点。

整理了「登录」、「授权」的概念。

然后分别针对「登录」介绍了一些关键的技术实现:

  1. Connexion silencieuse
  2. Gestion du statut asynchrone de la connexion silencieuse
  3. Traitement tolérant aux pannes de l'expiration de l'état de connexion personnalisé
  4. WeChatsession_keyTraitement tolérant aux pannes d'expiration

Quant à "l'autorisation", il y aura la logique de conception de la partie UI, et elle doit également impliquer le fractionnement des composants :

  1. Fractionnement des composants et conception
  2. Traitement de l'interception des autorisations

Ensuite, les interfaces back-end sur lesquelles repose ce schéma d'autorisation de connexion sont triées et le protocole de référence le plus simple est donné.

Enfin, du point de vue "dans le but de précipiter un ensemble de solutions et de services universels de connexion à un mini-programme", j'ai réglé la superposition au niveau architectural.

  1. Couche de personnalisation métier
  2. Couche de service de connexion
  3. Construction sous-jacente

Recommandations d'apprentissage gratuites associées : Développement du mini-programme WeChat

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!