微信小程序常見的兩種登錄方式詳解
小程序登錄
小程序有兩種登錄方式,一種基于手機(jī)號(hào)碼進(jìn)行登錄,另一種是使用用戶在公眾號(hào)下的唯一標(biāo)識(shí)(openid)進(jìn)行登錄(小程序是公眾號(hào)的一種).
接下來先講解下,基于 openid 登錄。
基于openid登錄
先看下圖,描述通過微信小程序提供的 code 換取當(dāng)前用戶在小程序中的唯一標(biāo)識(shí),詳細(xì)流程可以參數(shù)下圖:

接下來通過代碼實(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,
});
},
});
服務(wù)端接收 code 去微信后臺(tái)換取對(duì)應(yīng) 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ù)庫中查找對(duì)應(yīng) 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)注冊(cè)
models.user
.findOne({
where: {
openid,
},
})
.then((user) => {
if (user) {
// 3.2 如果用戶已經(jīng)注冊(cè),返回用戶信息
res.json(
new Result({
data: user,
msg: '登錄成功',
})
);
} else {
// 3.3 如果用戶沒有注冊(cè),創(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: '登錄成功',
})
);
});
}
});
});
上面就是一個(gè)基于 code 獲取 openid,并通過 openid 創(chuàng)建新的用戶,并將創(chuàng)建好的用戶返回。
為了方便理解,這里簡(jiǎn)化描述了登錄邏輯。在實(shí)際業(yè)務(wù)代碼中,通常會(huì)使用 openid 、 session key 和用戶信息來創(chuàng)建自定義登錄憑證(token),并在登錄時(shí)將用戶信息和 token 一起返回給前端。前端會(huì)將 token 存儲(chǔ)在本地,并在下一次需要登錄的業(yè)務(wù)請(qǐng)求中攜帶 token,從而實(shí)現(xiàn)業(yè)務(wù)鑒權(quán)的功能。這種方式通常使用 JWT(JSON Web Token)等工具來實(shí)現(xiàn)。在后續(xù)的講解中,我們將詳細(xì)介紹這些概念和技術(shù)細(xì)節(jié)。
手機(jī)號(hào)碼快捷登錄
獲取手機(jī)號(hào)碼的前提:
- 非個(gè)人小程序
- 認(rèn)證的小程序
- 非海外的企業(yè)認(rèn)證
下面是大概業(yè)務(wù)流程圖:

獲取對(duì)應(yīng)code
<template>
<button
class="login-btn"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
手機(jī)號(hào)碼登錄
</button>
</template>
<script>
export default {
setup() {
// 目前該接口針對(duì)非個(gè)人開發(fā)者,且完成了認(rèn)證的小程序開放(不包含海外主體)
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>后端處理邏輯
// 基于手機(jī)號(hào)登錄
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. 獲取手機(jī)號(hào)
const { phone_info } = await request.post(
`/wxa/business/getuserphonenumber?access_token=${access_token}`,
{
code,
}
);
// 4. 查找用戶是否已經(jīng)注冊(cè)
// 4.1 根據(jù) phone 查找用戶
const { purePhoneNumber } = phone_info;
models.user
.findOne({
where: {
purePhoneNumber,
},
})
.then((user) => {
if (user) {
// 4.2 如果用戶已經(jīng)注冊(cè),返回用戶信息
res.json(
new Result({
data: user,
msg: '登錄成功',
})
);
} else {
// 4.3 如果用戶沒有注冊(cè),創(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,
})
);
}
});上面代碼,實(shí)現(xiàn)獲取手機(jī)號(hào)碼并使用手機(jī)號(hào)碼作為唯一標(biāo)識(shí),進(jìn)行用戶創(chuàng)建和查找的操作。
從登錄的角度來看,使用手機(jī)號(hào)碼作為唯一標(biāo)識(shí)符是沒有問題的。然而,如果用戶嘗試使用非手機(jī)號(hào)碼(例如 OpenID)進(jìn)行登錄,并在數(shù)據(jù)庫中找不到匹配的記錄時(shí),系統(tǒng)會(huì)創(chuàng)建一個(gè)新的賬號(hào)。這可能導(dǎo)致同一個(gè)用戶在系統(tǒng)中存在多個(gè)賬號(hào)的情況。
為了優(yōu)化這種情況,可以考慮以下幾種方法:
- 當(dāng)用戶使用
openid登錄后,檢測(cè)未綁定手機(jī)號(hào)碼時(shí),進(jìn)行號(hào)碼綁定 - 當(dāng)用戶使用手機(jī)號(hào)碼登錄時(shí),提前調(diào)用
wx.login獲取對(duì)應(yīng)code,換取openid把他與手機(jī)號(hào)碼進(jìn)行關(guān)聯(lián)
現(xiàn)在基于上面的代碼,采用第二種方案,只需要微調(diào)下代碼就能解決這個(gè)問題。
- 登錄時(shí)把
wx.login獲取code傳遞給后端
<template>
<button
class="login-btn"
open-type="getPhoneNumber"
@getphonenumber="getPhoneNumber"
>
手機(jī)號(hào)碼登錄
</button>
</template>
<script>
export default {
setup() {
// 目前該接口針對(duì)非個(gè)人開發(fā)者,且完成了認(rèn)證的小程序開放(不包含海外主體)
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>服務(wù)端基于 loginCode 換取 openid
// 基于手機(jī)號(hào)登錄
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. 獲取手機(jī)號(hào)
const { phone_info } = await request.post(
`/wxa/business/getuserphonenumber?access_token=${access_token}`,
{
code,
openid,
}
);
// 5. 查找用戶是否已經(jīng)注冊(cè)
// 5.1 根據(jù) openid 查找用戶
models.user
.findOne({
where: {
openid,
},
})
.then((user) => {
if (user) {
// 5.2 如果用戶已經(jīng)注冊(cè),返回用戶信息
res.json(
new Result({
data: user,
msg: '登錄成功',
})
);
} else {
// 5.3 如果用戶沒有注冊(cè),創(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,
})
);
}
});這種方案被視為最佳的解決方案,能夠有效解決多賬號(hào)和綁定手機(jī)號(hào)碼等問題。 實(shí)際上,采用哪種方式取決于具體的業(yè)務(wù)場(chǎng)景,因?yàn)樵谀承┣闆r下,用戶可能會(huì)擔(dān)心手機(jī)號(hào)碼泄露而不愿采用這種方式。
注意
- 獲取手機(jī)號(hào)碼是需要收費(fèi),每次調(diào)用需要
0.03元。 wx.login與getPhoneNumber中獲取的code不是同一個(gè)
總結(jié)
- 基于 openid 或 手機(jī)號(hào)碼快捷登錄
- 獲取手機(jī)號(hào)碼前置條件
- 如何解決多賬號(hào)的問題
- 講解前端、后端、微信登錄過程中完整交互流程,方便更好去理解小程序登錄
以上就是微信小程序常見的兩種登錄方式詳解的詳細(xì)內(nèi)容,更多關(guān)于小程序登錄方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
BootStrap Table 設(shè)置height表頭與內(nèi)容無法對(duì)齊的問題
這篇文章主要介紹了BootStrap Table 設(shè)置height表頭與內(nèi)容無法對(duì)齊的問題,需要的朋友可以參考下2016-12-12
TypeScript開發(fā)小狀況記錄之選且只選一個(gè)
在開發(fā)中需要定義一個(gè)對(duì)象的類型,此類型必須包含某n個(gè)字段中的其中一種,這篇文章主要給大家介紹了關(guān)于TypeScript開發(fā)小狀況記錄之選且只選一個(gè)的相關(guān)資料,需要的朋友可以參考下2022-10-10
Javascript封裝DOMContentLoaded事件實(shí)例
這篇文章主要介紹了Javascript封裝DOMContentLoaded事件實(shí)例,DOMContentLoaded是FF,Opera 9的特有的Event, 當(dāng)所有DOM解析完以后會(huì)觸發(fā)這個(gè)事件,需要的朋友可以參考下2014-06-06
微信小程序swiper左右擴(kuò)展各顯示一半代碼實(shí)例
這篇文章主要介紹了微信小程序swiper左右擴(kuò)展各顯示一半代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
flash調(diào)用js中的方法,讓js傳遞變量給flash的辦法及思路
前幾天發(fā)表了 將FlashVars寫在JS函數(shù)中,實(shí)現(xiàn)與后臺(tái)的實(shí)時(shí)變量更新,但是僅支持 IE,隨后與 Luckyer 進(jìn)行了交流,發(fā)現(xiàn)用 SetVariable 可以很方便的實(shí)現(xiàn)多瀏覽器兼容。舉例如下。2013-08-08
Ionic如何實(shí)現(xiàn)下拉刷新與上拉加載功能
在日常項(xiàng)目開發(fā)中我們經(jīng)常見到下拉刷新上拉加載的功能,接下來通過本文給大家介紹ionic如何實(shí)現(xiàn)下拉刷新與上拉加載的相關(guān)資料,需要的朋友可以參考下2016-06-06
原生JS實(shí)現(xiàn)Tab欄切換功能的示例代碼
本文主要介紹了如何使用JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的商品展示頁面,包括前置知識(shí)點(diǎn)的講解、功能模塊的編寫以及易錯(cuò)點(diǎn)的總結(jié),如對(duì)象解構(gòu)、事件綁定和箭頭函數(shù)的使用等,需要的朋友可以參考下2026-04-04

