记录 uniapp 微信小程序登录流程

前言

记录 uniapp 微信小程序登录流程。

正文

之前做过一个基于 uniapp 的小程序,当时需要通过微信绑定的手机号来串联登录流程,在此做一个记录。

本文基于微信绑定的手机号来做唯一 ID ,非 openid

首先,每个微信小程序都会有一个 appidappsecret 。登录微信公众平台,选择小程序登录后即可看到,比如我微信账号的小程序测试号:

这两个东西都不放在前端代码中,特别是 appsecret ,后面的接口是要根据该字段来验证权限的。

前端获取 code

前端首先通过 uni.login 来获取 code ,这个 API 在微信平台上的接口为 wx.login 。这个 code 后面要用到。

1
2
3
4
const getCode = async () => {
const { code } = await uni.login({});
return code;
}

后端获取 openid

这一步要用到上一步的 code

后端调用微信的接口:GET https://api.weixin.qq.com/sns/jscode2session,这个接口需要 appidappsecret 和上一步的 code

然后我们可以得到 openid ,还有 unionid 等信息。这里我们只要 openid

后端获取 accessToken

这一步获取 accessToken ,后面的流程需要用到。

后端调用微信的接口:GET https://api.weixin.qq.com/cgi-bin/token,这个接口需要 appidappsecret

前端获取 phoneCode

这一步也是获取一个 code ,不过这个 code 是来获取手机信息的,这里就叫成 phoneCode

在微信的文档中,有两种获取该 phoneCode 的方式,区别为得到的手机号是否实时验证。

在之前的项目中,我们用的是第一个。在文档中我们需要按如下来触发获取 phoneCode

1
2
3
4
5
6
<button 
open-type="getPhoneNumber"
bindgetphonenumber="getPhoneNumber"
>
获取手机号
</button>

点击这个按钮就会弹出一个手机号码选择框:

这里可以根据 UI 来改为不同的触发形式,不一定是点击文字,你也可以在内部显示一个图片等等,把这里的 button 当成一个 view 就行,不过注意要清除 button 的默认样式:

1
2
3
4
5
6
7
8
9
10
11
.clearButtonStyle {
padding: 0;
margin: 0;
border: none;
appearance: none;
}

/* 去掉 button 的边框 */
.clearButtonStyle::after {
border: none;
}

然后我们在 getPhoneNumber 函数的内部就可以获取到 phoneCode

1
2
3
4
5
6
7
8
const getPhoneNumber = (e) => {
if (e.detail.errMsg === "getPhoneNumber:ok") {
// 这里就获取到了 phoneCode 了。
const phoneCode = e.detail.code;
} else {
// 可以做提示
}
}

后端获取手机号

根据前面获取的参数,在这一步我们就可以获取用户选择的手机号。

后端调用微信的接口:POST https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN,这个接口需要 openidaccessToken 和上一步的 phoneCode

在返回的参数中就能得到明文手机号:

接着就是用这个手机号验证用户是否存在,存在就下发一个 token ,不存在就用这个手机号创建一个用户,然后一样下发 token 。

后记

最后总结一下流程,起点为通过 buttongetPhoneNumber 方法:

1
2
3
4
5
6
7
8
9
10
const getPhoneNumber = async (e) => {
if (e.detail.errMsg === "getPhoneNumber:ok") {
const { code } = await uni.login({});
const phoneCode = e.detail.code;
// loginByWXApi 这个为后端的接口,非微信接口。
const { token, userInfo } = await loginByWXApi(code, phoneCode);
} else {
// 可以做提示
}
}

然后后端的流程(这里以 js 代码为例):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const appid = "xxx";
const appsecret = "xxx";

const loginByWXApi = async (code, phoneCode) => {
// 获取 openid
const { openid } = await fetch(`https://api.weixin.qq.com/sns/jscode2session?grant_type=authorization_code&appid=${appid}&secret=${appsecret}&js_code=${code}`).then(resp => resp.json());
// 获取 accessToken
const { access_token } = await fetch(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${appsecret}`).then(resp => resp.json());
// 获取手机号
const { phone_info } = await fetch(`https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=${access_token}`, {
methods: "POST",
body: JSON.stringify({
code: phoneCode,
openid,
})
}).then(resp => resp.json());
const { purePhoneNumber } = phone_info;
// 获取手机号后的流程...

return {
token: "xxx"
userInfo: "xxx"
}
}