微信小程序常見的兩種登錄方式詳解
小程序登錄
小程序有兩種登錄方式,一種基于手機號碼進行登錄,另一種是使用用戶在公眾號下的唯一標識(openid)進行登錄(小程序是公眾號的一種).
接下來先講解下,基于 openid 登錄。
基于openid登錄
先看下圖,描述通過微信小程序提供的 code 換取當前用戶在小程序中的唯一標識,詳細流程可以參數(shù)下圖:
接下來通過代碼實現(xiàn)下大概流程:
獲取 code
uni.login({ success: async (res) => { if (res.errMsg === 'login:ok') { const { data } = await login({ code: res.code, }); // 保存用戶信息 } }, fail(e) { uni.showToast({ title: e.message, }); }, });
服務端接收 code 去微信后臺換取對應 openid
// nodejs 部分代碼 const { appid, secret, grant_type } = require('../config/wx'); router.post('/login', (req, res) => { const { code } = req.query; const { appid, secret, grant_type } = require('../config/wx'); const { openid } = await request.get('/sns/jscode2session', { appid, secret, js_code: code, grant_type, }); });
在數(shù)據(jù)庫中查找對應 openid 是否存在
const { appid, secret, grant_type } = require('../config/wx'); router.post('/login', (req, res) => { // 1. 獲取 code const { code } = req.query; // 2. 通過 code 獲取 openid 和 session_key const { openid } = await request.get('/sns/jscode2session', { appid, secret, js_code: code, grant_type, }); // 3. 查找用戶是否已經(jīng)注冊 models.user .findOne({ where: { openid, }, }) .then((user) => { if (user) { // 3.2 如果用戶已經(jīng)注冊,返回用戶信息 res.json( new Result({ data: user, msg: '登錄成功', }) ); } else { // 3.3 如果用戶沒有注冊,創(chuàng)建用戶并返回用戶信息 const username = randomUserName(); models.user .create({ nickname: username, openid, avatar: '/uploads/default-avatar.png', }) .then((user) => { res.json( new Result({ data: user, msg: '登錄成功', }) ); }); } }); });
上面就是一個基于 code
獲取 openid
,并通過 openid
創(chuàng)建新的用戶,并將創(chuàng)建好的用戶返回。
為了方便理解,這里簡化描述了登錄邏輯。在實際業(yè)務代碼中,通常會使用 openid
、 session key
和用戶信息來創(chuàng)建自定義登錄憑證(token),并在登錄時將用戶信息和 token
一起返回給前端。前端會將 token
存儲在本地,并在下一次需要登錄的業(yè)務請求中攜帶 token
,從而實現(xiàn)業(yè)務鑒權的功能。這種方式通常使用 JWT(JSON Web Token)
等工具來實現(xiàn)。在后續(xù)的講解中,我們將詳細介紹這些概念和技術細節(jié)。
手機號碼快捷登錄
獲取手機號碼的前提:
- 非個人小程序
- 認證的小程序
- 非海外的企業(yè)認證
下面是大概業(yè)務流程圖:
獲取對應code
<template> <button class="login-btn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" > 手機號碼登錄 </button> </template> <script> export default { setup() { // 目前該接口針對非個人開發(fā)者,且完成了認證的小程序開放(不包含海外主體) const getPhoneNumber = (e) => { const { code, errMsg } = e.detail; if (errMsg === 'getPhoneNumber:ok') { const { data } = await loginByPhone({ code, }); } else { uni.showToast({ title: errMsg, }); } }; return { getPhoneNumber, }; }, }; </script>
后端處理邏輯
// 基于手機號登錄 router.post('/loginByPhone', async function (req, res) { try { // 1. 獲取 code 和 loginCode const { code } = req.body; // 2. 獲取接口調(diào)用憑據(jù),理論上這里需要緩存 access_token,避免頻繁調(diào)用接口 const { access_token } = await request.get('/cgi-bin/token', { grant_type: 'client_credential', appid, secret, }); // 3. 獲取手機號 const { phone_info } = await request.post( `/wxa/business/getuserphonenumber?access_token=${access_token}`, { code, } ); // 4. 查找用戶是否已經(jīng)注冊 // 4.1 根據(jù) phone 查找用戶 const { purePhoneNumber } = phone_info; models.user .findOne({ where: { purePhoneNumber, }, }) .then((user) => { if (user) { // 4.2 如果用戶已經(jīng)注冊,返回用戶信息 res.json( new Result({ data: user, msg: '登錄成功', }) ); } else { // 4.3 如果用戶沒有注冊,創(chuàng)建用戶并返回用戶信息 const username = randomUserName(); models.user .create({ nickname: username, avatar: '/uploads/default-avatar.png', phone: phone_info.purePhoneNumber, }) .then((user) => { res.json( new Result({ data: user, msg: '登錄成功', }) ); }); } }); } catch (error) { res.json( new Result({ code: 'BIZ_ERROR', msg: error.errmsg || error.message, }) ); } });
上面代碼,實現(xiàn)獲取手機號碼并使用手機號碼作為唯一標識,進行用戶創(chuàng)建和查找的操作。
從登錄的角度來看,使用手機號碼作為唯一標識符是沒有問題的。然而,如果用戶嘗試使用非手機號碼(例如 OpenID
)進行登錄,并在數(shù)據(jù)庫中找不到匹配的記錄時,系統(tǒng)會創(chuàng)建一個新的賬號。這可能導致同一個用戶在系統(tǒng)中存在多個賬號的情況。
為了優(yōu)化這種情況,可以考慮以下幾種方法:
- 當用戶使用
openid
登錄后,檢測未綁定手機號碼時,進行號碼綁定 - 當用戶使用手機號碼登錄時,提前調(diào)用
wx.login
獲取對應code
,換取openid
把他與手機號碼進行關聯(lián)
現(xiàn)在基于上面的代碼,采用第二種方案,只需要微調(diào)下代碼就能解決這個問題。
- 登錄時把
wx.login
獲取code
傳遞給后端
<template> <button class="login-btn" open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" > 手機號碼登錄 </button> </template> <script> export default { setup() { // 目前該接口針對非個人開發(fā)者,且完成了認證的小程序開放(不包含海外主體) const getPhoneNumber = (e) => { const { code, errMsg } = e.detail; if (errMsg === 'getPhoneNumber:ok') { uni.login({ success: async (res) => { if (res.errMsg === 'login:ok') { const { data } = await loginByPhone({ code, loginCode: res.code, }); userStore.setUserInfo(data); uni.navigateBack(); } }, fail(e) { uni.showToast({ title: e.message, }); }, }); } else { uni.showToast({ title: errMsg, }); } }; return { getPhoneNumber, }; }, }; </script>
服務端基于 loginCode 換取 openid
// 基于手機號登錄 router.post('/loginByPhone', async function (req, res) { try { // 1. 獲取 code 和 loginCode const { code, loginCode } = req.body; // 2. 獲取接口調(diào)用憑據(jù),理論上這里需要緩存 access_token,避免頻繁調(diào)用接口 const { access_token } = await request.get('/cgi-bin/token', { grant_type: 'client_credential', appid, secret, }); // 3. 獲取 openid const { openid } = await request.get('/sns/jscode2session', { appid, secret, js_code: loginCode, grant_type, }); // 4. 獲取手機號 const { phone_info } = await request.post( `/wxa/business/getuserphonenumber?access_token=${access_token}`, { code, openid, } ); // 5. 查找用戶是否已經(jīng)注冊 // 5.1 根據(jù) openid 查找用戶 models.user .findOne({ where: { openid, }, }) .then((user) => { if (user) { // 5.2 如果用戶已經(jīng)注冊,返回用戶信息 res.json( new Result({ data: user, msg: '登錄成功', }) ); } else { // 5.3 如果用戶沒有注冊,創(chuàng)建用戶并返回用戶信息 const username = randomUserName(); models.user .create({ nickname: username, openid, avatar: '/uploads/default-avatar.png', phone: phone_info.purePhoneNumber, }) .then((user) => { res.json( new Result({ data: user, msg: '登錄成功', }) ); }); } }); } catch (error) { res.json( new Result({ code: 'BIZ_ERROR', msg: error.errmsg || error.message, }) ); } });
這種方案被視為最佳的解決方案,能夠有效解決多賬號和綁定手機號碼等問題。 實際上,采用哪種方式取決于具體的業(yè)務場景,因為在某些情況下,用戶可能會擔心手機號碼泄露而不愿采用這種方式。
注意
- 獲取手機號碼是需要收費,每次調(diào)用需要
0.03
元。 wx.login
與getPhoneNumber
中獲取的code
不是同一個
總結
- 基于 openid 或 手機號碼快捷登錄
- 獲取手機號碼前置條件
- 如何解決多賬號的問題
- 講解前端、后端、微信登錄過程中完整交互流程,方便更好去理解小程序登錄
以上就是微信小程序常見的兩種登錄方式詳解的詳細內(nèi)容,更多關于小程序登錄方式的資料請關注腳本之家其它相關文章!
相關文章
Ant Design Pro 下實現(xiàn)文件下載的實現(xiàn)代碼
這篇文章主要介紹了Ant Design Pro 下實現(xiàn)文件下載的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-12-12定時器(setTimeout/setInterval)調(diào)用帶參函數(shù)失效解決方法
setInterval()方法可按照指定的周期(以毫秒計)來調(diào)用函數(shù)或計算表達式,setTimeout()方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計算表達式,詳細使用方法可以參考下本文2013-03-03