前言
在小程序中,与云开发相比,传统的前后端开发在登录鉴权的实现方面相对来说更加复杂,不仅需要前端和后端的交互,后端还需要与微信接口服务进行交互,以完成整个鉴权流程:
整个流程简单来说分为以下7步:
- 前端调用
wx.login()
获取临时登录凭证code
,并回传到开发者服务器。 - 服务器调用auth.code2Session换取用户唯一标识OpenID和会话密钥session_key。
- 服务器端根据
OpenID
或session_key
生成自定义登录态(可以理解为是token
),将token
响应给前端。 - 前端将
token
存入Storage
中。 - 当前端之后向后端发起请求时,就会带上
token
。 - 后台通过
token
(或者其他类型密钥),解密获取OpenID
,判断是哪个用户的行为,做出响应的逻辑处理(比如操作数据库等)。 - 后台响应数据给前端。
这个是小程序登录的流程,但是小程序登录和小程序获取用户信息并不是一回事。小程序登录的API
是wx.login
,可以获取用户的openID
,openID
是用户的唯一标识,是比较隐私的数据,一般不会返回给前端。小程序获取用户信息的API
是wx.getUserInfo
,它可以获取用户的一些基本信息,比如nickName
、avatarUrl
等。两者不要弄混。
其实微信登录一开始并不是这样的,以往微信小程序在用户没有任何操作的情况下就会直接弹出授权的登录方式,如果用户点击拒绝授权,则无法使用小程序。按照微信官方对这个功能更新的解释是:
![](https://delaprada-1301716802.cos.ap-guangzhou.myqcloud.com/20210126090858.png)
![](https://delaprada-1301716802.cos.ap-guangzhou.myqcloud.com/20210126091038.png)
因此,微信对开发的建议是:
- 当用户打开小程序时访问第一个页面时,先通过
wx.login
,获取用户openID
。这时无需弹框授权,开发者拿到openID
可以建立自身的帐号ID
。 - 在第一步中,拿到
openID
后,判断是新用户还是老用户。如果是老用户,可以直接登录;如果是新用户,可先在小程序首页展示你的信息服务,让用户对这个小程序有大概的了解,再引导用户进行下一步的操作。 - 当需要获取用户头像昵称的时候,对用户展示一个登录页面,这个页面只有一个最重要的操作,引导用户进行登录。
小程序登录
在上一节中有提到,小程序登录可以分为7个步骤,下面就详细讲一下7个步骤具体是如何实现。
step1: 前端调用wx.login()
获取临时登录凭证code
在项目中,我使用了Taro
框架,所以调用的API
对应为Taro.login()
:
Taro.login({})
.then((res) => {
if (res.code) {
// 将code发送到后台,以获取token
getToken(res.code)
.then((res: any) => {
const { token, userExist } = res;
// 将token存储到Storage中
Taro.setStorageSync('token', token);
// 如果是老用户,获取用户信息
if(userExist) {
const { userInfo } = res;
Taro.setStorageSync('userInfo', userInfo);
}
})
.catch((err) => {
console.error(err);
});
} else {
console.log('登录失败! ' + res.errMsg);
}
})
.catch((err) => {
console.error(err);
});
step2: 服务端调用auth.code2Session换取openid和session_key
服务端调用外部接口需要使用egg.js
中的一个api
:this.ctx.curl
,因为是异步请求,所以需要加上await
:
// app/controller/home.js
// login接口
async login() {
const {
ctx } = this;
const {
code } = ctx.request.body;
// 服务器根据客户端传来的code向微信接口服务获取session_key和openid
const res = await ctx.curl(
`https://api.weixin.qq.com/sns/jscode2session?
appid=wx6936c18b38186cf3&secret=d11f77fb7d5a959b6ba46c30dbd4da95&js_code=${
code}&grant_type=authorization_code`,
{
dataType: 'json',
}
);
const {
openid } = res.data; // 获取到openid
}
step3: 根据openid
生成自定义登录态token,响应给前端
因为openid
是用户的唯一标识,根据它生成token,响应给前端后,前端每次发请求带上token,后台解密请求中的token,获取到openid
,便能识别这是哪个用户的请求行为。
这里我们使用jwt
来生成自定义登录态token
,使用jwt-simple
库来生成jwt
:
const jwt = require('jwt-simple');
const SECRET = 'zhuoran'; // 自定义
async login() {
...
const {
openid } = res.data; // 获取到openid
// 根据用户的openid生成token
const token = jwt.encode(openid, SECRET);