Heim > Artikel > WeChat-Applet > Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung
Der Zweck eines solch sorgfältigen Designs für die Anmeldung/Registrierung besteht natürlich darin, diese als Grundfunktion der Anwendung robust genug zu machen, um eine Site-weite Blockierung zu vermeiden.
Gleichzeitig müssen wir vollständig darüber nachdenken, wie wir bei der Entwicklung neuer kleiner Programme die Wiederverwendungsfähigkeit schnell entfernen und wiederholte Fallstricke vermeiden können.
Das Anmelde- und Registrierungsmodul ist wie ein Eisberg. Wir glauben, dass es nur bedeutet: „Geben Sie Ihr Konto und Ihr Passwort ein, und schon sind Sie mit der Anmeldung fertig.“
Hier möchte ich mit allen hier einige Designerfahrungen und Ideen teilen, die ich gesammelt habe, nachdem ich kürzlich ein kleines Programm-Anmelde-/Registrierungsmodul abgeschlossen habe.
Beim Durchsuchen des Miniprogramms ist es aufgrund geschäftlicher Anforderungen häufig erforderlich, einige grundlegende Informationen des Benutzers einzuholen:
Produkte haben unterschiedliche Informationsanforderungen für Benutzer und verfügen über unterschiedliche Autorisierungsprozesse.
Der erste Typ ist in E-Commerce-Systemen üblich. Um das Multiplattform-Konto des Benutzers zu identifizieren, verwenden sie häufig ihre Mobiltelefonnummer, um einen Kontakt herzustellen. In diesem Fall muss der Benutzer eine Autorisierung durchführen die Mobiltelefonnummer.
Zweitens ist es für eine grundlegende Initialisierung der Benutzerinformationen häufig erforderlich, weitere Benutzerinformationen abzurufen, z. B. den WeChat-Spitznamen, unionId
usw., und Sie müssen um Benutzerautorisierung bitten.
Der dritte Typ umfasst den ersten Typ und den zweiten Typ.
Mit dem Ziel, eine Reihe universeller Mini-Programm-Login-Lösungen und -Dienste zu entwickeln, analysieren wir das Geschäft und ermitteln die Variablen.
Bevor Sie mit dem technischen Design beginnen, reden Sie über den nötigen Unsinn und führen Sie eine grundlegende Abstimmung einiger Konzepte durch.
Login ist auf Englisch „login“ und das entsprechende Wort ist „logout“. Bevor Sie sich anmelden, müssen Sie ein Konto haben und sich „registrieren“ (oder sich anmelden).
Es wird gesagt, dass das ursprüngliche Produkt keine Anmelde-/Registrierungsfunktion hatte, aber es wurde nach und nach verfügbar, als mehr Leute es nutzten. Aufgrund der Bedürfnisse des Produkts selbst muss der „Benutzer“ identifiziert werden.
In der realen Gesellschaft hat jeder von uns eine Identitäts-ID: einen Personalausweis. Als ich 16 wurde, führte ich eine „Registrierung“ durch, als ich zum ersten Mal zum Büro für öffentliche Sicherheit ging, um meinen Personalausweis zu bekommen. Dann ging ich ins Internetcafé, um im Internet zu surfen, zog meinen Ausweis durch und führte ein „Anmelde“-Verhalten durch.
Für das Internet in der virtuellen Welt lautet dieses Identitätszertifikat also „Konto + Passwort“.
Übliche Anmelde-/Registrierungsmethoden sind:
Konto- und Passwortregistrierung
In den frühen Tagen des Internets gab es nur eine geringe Abdeckung für persönliche E-Mails und Mobiltelefone. Daher muss sich der Benutzer einen Kontonamen ausdenken. Wir registrieren ein QQ-Konto, das in dieser Form vorliegt.
Registrierung von E-Mail-Adressen
Nach der Jahrtausendwende ist das PC-Internetzeitalter schnell populär geworden und wir haben alle unsere eigenen persönlichen E-Mail-Adressen erstellt. Darüber hinaus verfügt QQ auch über ein E-Mail-Konto. Da E-Mails persönlich und privat sind und Informationen kommunizieren können, haben die meisten Websites damit begonnen, E-Mail-Konten als Benutzernamen für die Registrierung zu verwenden. Während des Registrierungsvorgangs werden sie aufgefordert, sich bei der entsprechenden E-Mail-Adresse anzumelden, um nach Aktivierungs-E-Mails zu suchen und so sicherzustellen, dass wir Eigentümer der registrierten E-Mail-Adresse sind.
Registrierung von Mobiltelefonnummern
Nach der Popularität des Internets haben sich Smartphones und mobiles Internet rasant weiterentwickelt. Mobiltelefone sind zu einem unverzichtbaren Mobilgerät für jedermann geworden, und das mobile Internet ist tief in das moderne Leben aller Menschen integriert. Daher sind Mobiltelefonnummern im Vergleich zu E-Mails derzeit enger mit Einzelpersonen verknüpft, und es entstehen immer mehr mobile Anwendungen, und auch Registrierungsmethoden, die Mobiltelefonnummern als Benutzernamen verwenden, sind weit verbreitet.
Bis 2020 erreichte die Zahl der WeChat-Nutzer 1,2 Milliarden. Nun, WeChat-Konten sind, zumindest in China, zum „Identitätszeichen“ der neuen Generation der Internetwelt geworden.
Für das WeChat-Applet ist es selbstverständlich, die WeChat-Konto-ID des aktuellen Benutzers zu kennen. WeChat ermöglicht es Miniprogrammanwendungen, sich stillschweigend bei unseren Miniprogrammanwendungen anzumelden, ohne dass der Benutzer es merkt. Dies wird oft als „stille Anmeldung“ bezeichnet.
Tatsächlich ist die Anmeldung des WeChat-Applets im Wesentlichen das gleiche Konzept wie die „Single Sign-On“ herkömmlicher Webanwendungen.
Da HTTP ursprünglich zustandslos ist, lautet der grundlegende allgemeine Ansatz der Branche für den Anmeldestatus:
Im WeChat-Applet ist die „JS-Logikschicht“ keine Browserumgebung und es gibt natürlich kein Cookie
, daher wird normalerweise die Methode access token
verwendet. Cookie
,那么通常会使用 access token
的方式。
对于需要更进一步获取用的用户昵称、用户手机号等信息的产品来说。微信出于用户隐私的考虑,需要用户主动同意授权。小程序应用才能获取到这部分信息,这就有了目前流行的小程序「Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung」、「Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung」的交互了。
出于不同的用户信息敏感度不同的考虑,微信小程序对于不同的用户信息提供「授权」的方式不尽相同:
wx.getLocation()
的时候,如果用户未授权,则会弹出地址授权界面。wx.getLocation()
直接返回失败。<button open-type="xxx"></button>
方式。wx.authorize()
,提前询问授权,之后需要获取相关信息的时候不用再次弹出授权。梳理清楚了概念之后,我们模块的划分上,可以拆分为两大块:
Session
Auth
微信官方提供的登录方案,总结为三步:
wx.login()
获取一次性加密凭证 code,交给后端。openId
和授权凭证 session_key
。(用于后续服务器端和微信服务器的特殊 API 调用,具体看:微信官方文档-服务端获取开放数据)。如果只是实现这个流程的话,挺简单的。
但要实现一个健壮的登录过程,还需要注意更多的边界情况:
收拢 wx.login()
的调用:
由于 wx.login()
会产生不可预测的副作用,例如会可能导致session_key
失效,从而导致后续的授权解密场景中的失败。我们这里可以提供一个像 session.login()
的方法,掌握 wx.login()
控制权,对其做一系列的封装和容错处理。
调用的时机:
通常我们会在应用启动的时候( app.onLaunch()
wx.getLocation()
aufgerufen wird und der Benutzer nicht autorisiert ist, wird die Adressautorisierungsschnittstelle angezeigt. 🎜🎜Bei Ablehnung wird das Fenster nicht erneut angezeigt und wx.getLocation()
gibt direkt einen Fehler zurück. 🎜🎜🎜🎜<button open-type="xxx"></button>
Methode. 🎜🎜Unterstützt nur: vertrauliche Benutzerinformationen und die Mobiltelefonnummer des Benutzers. Sie müssen mit dem Backend zusammenarbeiten, um die Daten symmetrisch zu ver- und entschlüsseln. 🎜🎜Der Benutzer hat es abgelehnt. Wenn Sie erneut auf die Schaltfläche klicken, wird das Popup-Fenster weiterhin angezeigt. 🎜🎜🎜🎜Verwenden Sie wx.authorize()
, um im Voraus eine Autorisierung anzufordern. Es ist nicht nötig, die Autorisierung erneut aufzurufen, wenn Sie später relevante Informationen benötigen. 🎜🎜Session
🎜🎜Autorisierung: Verantwortlich für die Interaktion mit Benutzer, Informationen abrufen und aktualisieren sowie Berechtigungen steuern usw. Das Modul heißt: Auth
🎜🎜openId
und das Autorisierungszertifikat session_key
. (Spezielle API-Aufrufe, die für nachfolgende serverseitige und WeChat-Server verwendet werden, siehe für Details: Offizielle WeChat-Dokumentation – serverseitige Erfassung offener Daten). 🎜🎜Das Backend übermittelt die vom WeChat-Server erhaltenen Benutzeranmeldeinformationen und die selbst generierten Anmeldeinformationen (Token) an das Frontend. Das Frontend speichert es und bringt es bei der nächsten Anfrage zum Backend, damit es identifizieren kann, um welchen Benutzer es sich handelt. 🎜🎜🎜Wenn Sie diesen Prozess einfach umsetzen, ist er ganz einfach. 🎜🎜Aber um einen robusten Anmeldeprozess zu implementieren, müssen Sie auf weitere Randfälle achten: 🎜🎜🎜🎜Reduzieren Sie den Aufruf von wx.login()
: 🎜🎜 Da wx.login()
unvorhersehbare Nebenwirkungen hervorruft, kann es beispielsweise dazu führen, dass session_key
ungültig wird, was in nachfolgenden Autorisierungsentschlüsselungsszenarien zu Fehlern führt. Wir können hier eine Methode wie session.login()
bereitstellen, um die Kontrolle über wx.login()
zu übernehmen und eine Reihe von Kapselungs- und Fehlertoleranzverarbeitungen darauf durchzuführen. 🎜🎜🎜🎜Zeitpunkt des Aufrufs: 🎜🎜Normalerweise initiieren wir die stille Anmeldung, wenn die Anwendung startet ( app.onLaunch()
). Es tritt jedoch ein asynchrones Problem auf, das durch das Designproblem des Applet-Lebenszyklus verursacht wird: Beim Laden der Seite und beim Aufrufen einer Back-End-API, die einen Anmeldestatus erfordert, wird der vorherige asynchrone statische Anmeldevorgang möglicherweise nicht abgeschlossen, was zu einem Anforderungsfehler führt . 🎜Natürlich können Sie einen Anmeldeaufruf auch asynchron blockierend initiieren, wenn die erste Schnittstelle aufgerufen wird, die den Anmeldestatus erfordert. Dies erfordert eine gut gestaltete Schnittstellenschicht.
Die detaillierten Gestaltungsideen für die beiden oben genannten Szenarien werden im Folgenden besprochen.
Probleme bei gleichzeitigen Anrufen:
In Geschäftsszenarien ist es unvermeidlich, dass mehrere Codes die Anmeldung auslösen müssen. In extremen Fällen initiieren diese mehreren Codes gleichzeitig Anrufe. Dies führt dazu, dass der Anmeldevorgang innerhalb kurzer Zeit mehrmals gestartet wird, obwohl die vorherige Anfrage noch nicht abgeschlossen wurde. In dieser Situation können wir den ersten Anruf blockieren und auf das Ergebnis nachfolgender Anrufe warten, genau wie bei der Kombination von Sperma und Eizellen.
Probleme mit nicht abgelaufenen Anrufen:
Wenn unser Anmeldestatus nicht abgelaufen ist und normal verwendet werden kann, besteht standardmäßig keine Notwendigkeit, den Anmeldevorgang einzuleiten. Zu diesem Zeitpunkt können wir zunächst prüfen, ob der Anmeldestatus standardmäßig verfügbar ist. Wenn nicht, können wir eine Anfrage initiieren. Dann können Sie auch einen Parameter ähnlich wie session.login({ force: true })
bereitstellen, um die Anmeldung zu erzwingen. session.login({ force: true })
的参数去强行发起登录。
1. 应用启动的时候调用
因为大部分情况都需要依赖登录态,我们会很自然而然的想到把这个调用的时机放到应用启动的时候( app.onLaunch()
)来调用。
但是由于原生的小程序启动流程中, App
,Page
,Component
的生命周期钩子函数,都不支持异步阻塞。
那么我们很容易会遇到 app.onLaunch
发起的「登录过程」在 page.onLoad
的时候还没有完成,我们就无法正确去做一些依赖登录态的操作。
针对这种情况,我们设计了一个Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung的工具:status
基于Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung,我们就可以编写这样的代码:
import { Status } from '@beautywe/plugin-status';// on app.jsApp({ status: { login: new Status('login'); }, onLaunch() { session // 发起静默登录调用 .login() // 把Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung设置为 success .then(() => this.status.login.success()) // 把Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung设置为 fail .catch(() => this.status.login.fail()); }, });// on page.jsPage({ onLoad() { const loginStatus = getApp().status.login; // must 里面会进行状态的判断,例如登录中就等待,登录成功就直接返回,登录失败抛出等。 loginStatus().status.login.must(() => { // 进行一些需要登录态的操作... }); }, });复制代码
2. 在「第一个需要登录态接口」被调用的时候去发起登录
更进一步,我们会发现,需要登录态的更深层次的节点是在发起的「需要登录态的后端 API 」的时候。
那么我们可以在调用「需要登录态的后端 API」的时候再去发起「静默登录」,对于并发的场景,让其他请求等待一下就好了。
以 fly.js 作为 wx.request()
封装的「网络请求层」,做一个简单的例子:
// 发起请求,并表明该请求是需要登录态的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; });复制代码
当自定义登录态过期的时候,后端需要返回特定的状态码,例如:AUTH_EXPIRED
、 AUTH_INVALID
等。
前端可以在「网络请求层」去监听所有请求的这个状态码,然后发起刷新登录态,再去重放失败的请求:
// 添加响应拦截器fly.interceptors.response.use( (response) => { const code = res.data; // 登录态过期或失效 if ( ['AUTH_EXPIRED', 'AUTH_INVALID'].includes(code) ) { // 刷新登录态 await session.refreshLogin(); // 然后重新发起请求 return fly.request(request); } } )复制代码
那么如果并发的发起多个请求,都返回了登录态失效的状态码,上述代码就会被执行多次。
我们需要对 session.refreshLogin()
做一些特殊的容错处理:
示例代码:
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>
我们从上面的「静默登录」之后,微信服务器端会下发一个 session_key
给后端,而这个会在需要获取Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung的时候会用到。
而 session_key
1 Wird beim Start der Anwendung aufgerufen, da die meisten Situationen Abhängigkeiten im Anmeldestatus erfordern. Wir denken natürlich darüber nach, diesen Aufruf beim Start der Anwendung aufzurufen (
app.onLaunch()
).Aufgrund des nativen Applet-Startvorgangs unterstützen die Lebenszyklus-Hook-Funktionen von
🎜Dann können wir leicht feststellen, dass der vonApp
,Page
undComponent
jedoch kein asynchrones Blockieren .app.onLaunch
initiierte „Anmeldevorgang“ zum Zeitpunkt vonpage.onLoad
noch nicht abgeschlossen ist und wir dies nicht können um einige Abhängigkeiten korrekt durchzuführen. 🎜🎜Für diese Situation haben wir ein Zustandsmaschinentool entwickelt: status🎜🎜🎜🎜Basierend auf der Zustandsmaschine können wir Code wie diesen schreiben:🎜// 定义检查 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' }); } } )复制代码🎜🎜2. Im „Kapitel Wenn an Wenn die Schnittstelle aufgerufen wird, die den Anmeldestatus erfordert, wird die Anmeldung initiiert. 🎜🎜🎜Darüber hinaus werden wir feststellen, dass der tiefere Knoten, der den Anmeldestatus erfordert, der Zeitpunkt ist, an dem die „Backend-API, die den Anmeldestatus erfordert“ initiiert wird. 🎜🎜 Dann können wir die „stille Anmeldung“ initiieren, wenn wir die „Backend-API, die den Anmeldestatus erfordert“ aufrufen. Lassen Sie bei gleichzeitigen Szenarien einfach andere Anfragen warten. 🎜🎜Verwenden Sie fly.js als „Netzwerkanforderungsschicht“, gekapselt durchwx.request()
, und erstellen Sie ein einfaches Beispiel: 🎜// 用户登录的阶段export enum AuthStep { // 阶段一:只有登录态,没有用户信息,没有手机号 ONE = 1, // 阶段二:有用户信息,没有手机号 TWO = 2, // 阶段三:有用户信息,有手机号 THREE = 3, }复制代码3.1.3 Fehlertolerante Verarbeitung des Ablaufs des benutzerdefinierten Anmeldestatus
🎜Wenn der benutzerdefinierte Anmeldestatus abläuft, muss das Backend einen bestimmten Statuscode zurückgeben, wie zum Beispiel:AUTH_EXPIRED
,AUTH_INVALID
warte. 🎜🎜Das Frontend kann den Statuscode aller Anfragen in der „Netzwerkanforderungsschicht“ überwachen, dann eine Aktualisierung des Anmeldestatus initiieren und dann die fehlgeschlagene Anfrage erneut abspielen: 🎜// 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) { // 更新全局的授权Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung,广播消息给订阅者。 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 }); }, // ...});复制代码🎜 Wenn dann mehrere Anfragen gleichzeitig initiiert werden, erfolgt die Anmeldung Status wird zurückgegeben. Ungültiger Statuscode, der obige Code wird mehrmals ausgeführt. 🎜🎜Wir müssen eine spezielle Fehlertoleranzverarbeitung fürsession.refreshLogin()
durchführen: 🎜🎜🎜Anfragesperre🎜: Es ist nur eine laufende Netzwerkanfrage gleichzeitig zulässig. 🎜🎜🎜Wartewarteschlange🎜: Nachdem die Anforderung gesperrt wurde, werden alle Aufrufe dieser Methode in eine Warteschlange verschoben und warten darauf, dass die Netzwerkanforderung abgeschlossen wird, um das Rückgabeergebnis zu teilen. 🎜🎜🎜Leistungsschaltermechanismus🎜: Bei mehrmaligem Aufruf innerhalb kurzer Zeit reagiert es für einen bestimmten Zeitraum nicht mehr, ähnlich wie beim langsamen TCP-Start. 🎜
🎜Beispielcode: 🎜<view> <!-- 已完成授权 --> <block> <view>已完成授权</view> </block> <!-- 未完成授权,第一步:Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung --> <block> <user-container> <view>Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung</view> </user-container> </block> <!-- 未完成授权,第二步:Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung --> <block> <phone-container> <view>Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung</view> </phone-container> </block> </view>复制代码3.1.4 Fehlertolerante Verarbeitung des Ablaufs des WeChat-Sitzungsschlüssels
🎜Nachdem wir mit der obigen „stillen Anmeldung“ begonnen haben, Der WeChat-Server Einsession_key
wird an das Backend gesendet und dieser wird verwendet, wenn es notwendig ist, offene WeChat-Daten zu erhalten. 🎜🎜🎜🎜Undsession_key
ist zeitkritisch. Das Folgende stammt aus der offiziellen Beschreibung von WeChat: 🎜🎜🎜Session key session_key validity🎜🎜Wenn Entwickler darauf stoßen Wenn die Signaturüberprüfung oder -entschlüsselung fehlschlägt, weil der Sitzungsschlüssel falsch ist, beachten Sie bitte die folgenden Vorsichtsmaßnahmen in Bezug auf Sitzungsschlüssel. 🎜
- wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key 失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key 刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储的 session_key。
- 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key 进行续期。用户越频繁使用小程序,session_key 有效期越长。
- 开发者在 session_key 失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验 session_key 是否有效,从而避免小程序反复执行登录流程。
- 当开发者在实现自定义登录态时,可以考虑以 session_key 有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
翻译成简单的两句话:
session_key
时效性由微信控制,开发者不可预测。wx.login
可能会导致 session_key
过期,可以在使用接口之前用 wx.checkSession
检查一下。而对于第二点,我们通过实验发现,偶发性的在 session_key
已过期的情况下,wx.checkSession
会概率性返回 true
社区也有相关的反馈未得到解决:
所以结论是:wx.checkSession
可靠性是不达 100% 的。
基于以上,我们需要对 session_key
的过期做一些容错处理:
session_key
的请求前,做一次 wx.checkSession
操作,如果失败了刷新登录态。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' }); } } )复制代码
在用户信息和手机号获取的方式上,微信是以 <button open-type="'xxx'"></button>
的方式,让用户主动点击授权的。
那么为了让代码更解耦,我们设计这样三个组件:
<user-contaienr getuserinfo="onUserInfoAuth"></user-contaienr>
: 包装点击交互,通过 <slot></slot>
支持点击区域的自定义UI。<phone-container getphonennmber="onPhoneAuth"></phone-container>
: 与 <user-container></user-container>
同理。<auth-flow></auth-flow>
: 根据业务需要,组合 <user-container></user-container>
、<phone-container></phone-container>
组合来定义不同的授权流程。以开头的业务场景的流程为例,它有这样的要求:
那么授权的阶段可以分三层:
// 用户登录的阶段export enum AuthStep { // 阶段一:只有登录态,没有用户信息,没有手机号 ONE = 1, // 阶段二:有用户信息,没有手机号 TWO = 2, // 阶段三:有用户信息,有手机号 THREE = 3, }复制代码
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) { // 更新全局的授权Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung,广播消息给订阅者。 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 }); }, // ...});复制代码
那么我们的 <auth-flow></auth-flow>
中就可以根据 currAuthStep
和 mustAuthStep
来去做不同的 UI 展示。需要注意的是使用 <user-container></user-container>
、<phone-container></phone-container>
的时候连接上 nextStep(e)
函数。
示例伪代码:
<view> <!-- 已完成授权 --> <block> <view>已完成授权</view> </block> <!-- 未完成授权,第一步:Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung --> <block> <user-container> <view>Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung</view> </user-container> </block> <!-- 未完成授权,第二步:Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung --> <block> <phone-container> <view>Verstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung</view> </phone-container> </block> </view>复制代码
到这里,我们制作好了用来承载授权流程的组件 <auth-flow></auth-flow>
,那么接下来就是决定要使用它的时机了。
我们梳理需要授权的场景:
点击某个按钮,例如:购买某个商品。
对于这种场景,常见的是通过弹窗完成授权,用户可以选择关闭。
浏览某个页面,例如:访问个人中心。
对于这种场景,我们可以在点击跳转某个页面的时候,进行拦截,弹窗处理。但这样的缺点是,跳转到目标页面的地方可能会很多,每个都拦截,难免会错漏。而且当目标页面作为「小程序落地页面」的时候,就避免不了。
这时候,我们可以通过重定向到授权页面来完成授权流程,完成之后,再回来。
那么我们定义一个枚举变量:
// 授权的展示形式export enum AuthDisplayMode { // 以弹窗形式 POPUP = 'button', // 以页面形式 PAGE = 'page', }复制代码
我们可以设计一个 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>
那么我们就能在按钮点击,或者页面加载的时候进行授权拦截:
Page({ onLoad() { session.mustAuth().then(() => { // 开始初始化页面... }); } onClick(e) { session.mustAuth().then(() => { // 开始处理回调逻辑... }); } })复制代码
当然,如果项目使用了 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); }); }; }; }复制代码
那么使用方式就简单一些了:
Page({ @mustAuth(); onLoad() { // 开始初始化页面... } @mustAuth(); onClick(e) { // 开始处理回调逻辑... } });复制代码
作为一套可复用的小程序登录方案,当然需要去定义好前后端的交互协议。
那么整套登录流程下来,需要的接口有这么几个:
静默登录 silentLogin
token
给前端token
前端会存起来,每个请求都会带上nickname
和phone
字段,前端用于计算当前用户的授权阶段。当然这个状态的记录可以放在后端,但是我们认为放在前端,会更加灵活。更新用户信息 updateUser
iv
, encryptedData
unionId
等nickname
等用户基本信息。session
中,用于计算授权阶段。更新用户手机号 updatePhone
iv
, encryptedData
session
中,用于计算授权阶段。解绑手机号 unbindPhone
登录 logout
入参:-
出参:-
说明:后端主动过期登录态,成功与否,走业务定义的前后端协议。
最后我们来梳理一下整体的「登录服务」的架构图:
由「登录服务」和「底层建设」组合提供的通用服务,业务层只需要去根据产品需求,定制授权的流程 <auth-flow></auth-flow>
,就能满足大部分场景了。
本篇文章通过一些常见的登录授权场景来展开来描述细节点。
整理了「登录」、「授权」的概念。
然后分别针对「登录」介绍了一些关键的技术实现:
session_key
AblauffehlertoleranzverarbeitungWas die „Autorisierung“ betrifft, gibt es die Logik zum Entwerfen des UI-Teils. was auch einbezogen werden muss Zur Aufteilung von Komponenten:
Dann werden die Back-End-Schnittstellen, auf denen dieses Anmeldeautorisierungsschema basiert, aussortiert und die einfachste Referenz erstellt Protokoll liegt vor.
Abschließend habe ich aus der Perspektive „mit dem Ziel, eine Reihe universeller Mini-Programm-Login-Lösungen und -Dienste zu entwickeln“ die Schichtung auf architektonischer Ebene geklärt.
Verwandte kostenlose Lernempfehlungen: WeChat-Applet-Entwicklung
Das obige ist der detaillierte Inhalt vonVerstehen Sie das Front-End-Design und die Implementierung der WeChat-Miniprogramm-Anmeldung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!