提供用户登录以及维护用户的登录状态,是一个拥有用户系统的软件应用普遍需要做的事情。像微信这样的一个社交平台,如果做一个小程序应用,我们可能很少会去做一个完全脱离和舍弃连接用户信息的纯工具软件。
让用户登录,标识用户和获取用户信息,以用户为核心提供服务,是大部分小程序都会做的事情。我们今天就来了解下在小程序中,如何做用户登录,以及如何去维护这个登录后的会话(Session)状态。
在微信小程序中,我们大致会涉及到以下三类登录方式:
- 自有的账号注册和登录
- 使用其他第三方平台账号登录
- 使用微信账号登录(即直接使用当前已登录的微信账号来作为小程序的用户进行登录)
第一和第二种方式是目前Web应用中最常见的两种方式,在微信小程序中同样可以使用,但是需要值的注意的是,小程序中没有Cookie
的机制,所以在使用这2种方式前,请确认你们或第三方的API是否需要依赖Cookie
;还有小程序中也不支持HTML页面,那些需要使用页面重定向来进行登录的第三方API就需要改造,或不能用了。
我们今天主要来讨论一下第三种方式,即如何使用微信账号进行登录,因为这种方式和微信平台结合最紧密,用户体验比较好。
登录流程
引用小程序官方文档的登录流程图,整个登录流程基本如下图所示:

该图中,“小程序”指的就是我们使用小程序框架写的代码部分,“第三方服务器”一般就是我们自己的后台服务程序,“微信服务器”是微信官方的API服务器。
下面我们来逐步分解一下这个流程图。
步骤1:在客户端获取当前登录微信用户的登录凭证(code)
在小程序中登录的第一步,就是先获取登录凭证。我们可以使用wx.login()方法并得到一个登录凭证。
我们可以在小程序的App代码中发起登录凭证请求,也可以在其他任何Page页面代码中发起登录凭证请求,主要根据你小程序的实际需要。
App({
onLaunch: function() {
wx.login({
success: function(res) {
var code = res.code;
if (code) {
console.log('获取用户登录凭证:' + code);
} else {
console.log('获取用户登录态失败:' + res.errMsg);
}
}
});
}
})
步骤2:将登录凭证发往你的服务端,并在你的服务端使用该凭证向微信服务器换取该微信用户的唯一标识(openid)
和会话密钥(session_key)
首先,我们使用wx.request()方法,请求我们自己实现的一个后台API,并将登录凭证(code)携带过去,例如在我们前面代码的基础上增加:
App({
onLaunch: function() {
wx.login({
success: function(res) {
var code = res.code;
if (code) {
console.log('获取用户登录凭证:' + code);
// --------- 发送凭证 ------------------
wx.request({
url: 'https://www.my-domain.com/wx/onlogin',
data: { code: code }
})
// ------------------------------------
} else {
console.log('获取用户登录态失败:' + res.errMsg);
}
}
});
}
})
你的后台服务(/wx/onlogin)接着需要使用这个传递过来的登录凭证,去调用微信接口换取openid和session_key,接口地址格式如下所示:
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
这里是我使用了Node.js Express构建的后台服务的代码,仅供参考:
router.get('/wx/onlogin', function (req, res, next) {
let code = req.query.code
request.get({
uri: 'https://api.weixin.qq.com/sns/jscode2session',
json: true,
qs: {
grant_type: 'authorization_code',
appid: '你小程序的APPID',
secret: '你小程序的SECRET',
js_code: code
}
}, (err, response, data) => {
if (response.statusCode === 200) {
console.log("[openid]", data.openid)
console.log("[session_key]", data.session_key)
//TODO: 生成一个唯一字符串sessionid作为键,将openid和session_key作为值,存入redis,超时时间设置为2小时
//伪代码: redisStore.set(sessionid, openid + session_key, 7200)
res.json({ sessionid: sessionid })
} else {
console.log("[error]", err)
res.json(err)
}
})
})
这段后台代码成功执行的话,就可以得到openid和session_key。这个信息就是当前微信账户在微信服务器那边的登录态了。
但是,为了安全方面的原因,请不要直接使用这些信息作为你小程序的用户标识和session标识回传到小程序客户端中去,我们应该在服务器端做一层自己的session,将这个微信账号登录态生成一个session id并维护在我们自己的session机制中,然后把这个session id派发到小程序客户端作为session标识来使用。
关于如何在服务器端做这个session机制,我们现在一般采用键值对存储工具来做,比如redis。我们为每个session生成一个唯一的字符串作为键,然后可以将session_key和openid作为值,存入redis中,为了安全,存入的时候还应设置一个超时的时间。
步骤3:在客户端保存sessionid
开发Web应用的时候,在客户端(浏览器)中,我们通常将session id存放在cookie中,但是小程序没有cookie机制,所以不能采用cookie了,但是小程序有本地的storage,所以我们可以使用storage来保存sessionid,以供后续的后台API调用所使用。
在之后,调用那些需要登录后才有权限的访问的后台服务时,你可以将保存在storage中的sessionid取出并携带在请求中(可以放在header中携带,也可以放在querystring中,或是放在body中,根据你自己的需要来使用),传递到后台服务,后台代码中获取到该sessionid后,从redis中查找是否有该sessionid存在,存在的话,即确认该session是有效的,继续后续的代码执行,否则进行错误处理。
这是一个需要session验证的后台服务示例,我的sessionid是放在header中传递的,所以在这个示例中,是从请求的header中获取sessionid:
router.get('/wx/products/list', function (req, res, next) {
let sessionid = req.header("sessionid")
let sessionVal = redisStore.get(sessionid)
if (sessionVal) {
// 执行其他业务代码
} else {
// 执行错误处理
}
})
好了,通过微信账号进行小程序登录和状态维护的简单流程就是这样,了解这些知识点之后,再基于此进行后续的开发就会变得更容易了。
另外,腾讯前端团队也开源了他们封装的相关库Wafer,可以借鉴和使用。
- 服务端SDK: wafer-node-session
- 小程序端SDK: wafer-client-sdk
感谢阅读我的文章,如有疑问或写错的地方请不吝留言赐教。
网友评论
现在可以跳首页,但不能清除用户信息
恩这个又看,但是这个二维码需要怎么生成呢?我用指定的page
你可以参考下文章最后提到的Wafer这个框架的代码。
var user = new User({
openid: data.openid,
nickName: param.nickname,
avatarUrl: param.head_img,
creatAt: param.creatAt,
token: createToken()
})
return user.save();
为什么是 return user.save();呢?这块的写法也是不对的,你应该res.json输出内容才对吧
if (info) {
console.info('用户已经存在');
console.info(info.token)
return res.send(token);
}
1、小程序的sessionKey过期了可是我的3rd session没有过期,这个时候拿着老的3rd session还是可以进行相关业务操作,这个时候要怎么同步?
2、我的3rd session过期了,可是小程序的sessionKey还没过期,这个时候需要重新签发一个新的3rd session吗?
同时服务器redis过期时也要告诉前台重新登录,再次更新是这样吗?我不知道如果后台过期了假如设置半个小时过期 那么告诉小程序说过期了,那他重新登录的时候会不会还是提示用户重新登录,不知道后台的redis过期时间设置多少比较合理,上文评价的我们不知道小程序自己的过期时间,微信不告诉我们具体多好,那怎么样设置时间更合理呢
- 每次小程序前端调用后端服务前,使用wx.checkSession()来检查微信登录有效性(也就是你说的session_key是否过期);
- session_key和3rd session任意一个过期,则把两者都重新建立一遍
如果希望在退出微信小程序后再次进入,能够回到退出之前的页面(比如用户正在阅读文章,此时有消息,那么暂时退出小程序),可以实现么?
我这里想了一下,是不是可以在GlobalData里保存当前显示的页面,等到用户再次进入时,从GlobalData中得到页面并重新加载。但是因为微信小程序在进入后台超过5分钟之后就会被销毁,所以这个GlobalData是不是也没有意义了?
您有什么建议么?非常感谢~~
如果你还是需要自己来做,那么保存在globalDate里是不可行的。你可以把数据保存在storage里。
1. 可以肯定的是,杀掉微信进程后,storage里面的值是不会被清空的,只有把这个微信小程序删除的情况下,才会清空storage值。
2. 你从storage里面获取了这个状态值,如果它是weixin或是sk,你会做什么处理?会有后续的登录操作么?
再进去就是说,我在真机上,退出小程序,然后再到后台杀掉微信的进程。再进小程序的时候,它就又要我登陆了。我不知道是不是在杀掉微信进程后,存的storage的值也会被清空
我在storage里面存的就是一个状态值,weixin代表用户上一次是微信登陆,sk代表上一次是自己的账号密码登陆。如果这两个值都没取到,就代表没有他的登陆记录,就需要登陆。
1.你说的“再进去就不行了”,是个什么样的表现?
2.你在storage中存储的是些什么样的值?
2.你这种方法在不考虑安全性的前提下,也是可以达到功能的啦。
自己维护的session用于维护用户在小程序中的登录态么?
你可以参考下腾讯的一个开源小程序维持会话的客户端SDK的源码和做法(https://github.com/tencentyun/wafer-client-sdk),就可以进一步了解了。
假设我的小程序有 5 个页面,每个页面都必须让用户已登录状态访问,这样的话,我是不是得给每个页面写一个「先检查是否登录,如果未登录的话,发起登录」的逻辑,一共写五次?
不管你改微信的各种信息也好,还是退出了重新登录微信也好,只要是同一个微信号,它是有唯一标识的(unionId / openId),所以可以根据这个ID去准确匹配从小程序中拿到的用户信息和你数据库中的用户信息的。
你可以再仔细想一下。
“9.5若小程序中存在帐号关系或付费内容,需提供测试号,包含帐号和密码,保证审核者可以体验所有功能。”