app
.post('/user/bindinfo', (req, res) => {
var user = req.user
if (user) {
var {encryptedData, iv} = req.body
var pc = new WXBizDataCrypt(config.appId, user.sessionKey)
var data = pc.decryptData(encryptedData, iv)
Object.assign(user, data)
return res.send({
code: 0
})
}
throw new Error('用户未登录')
})
.post('/user/bindphone', (req, res) => {
var user = req.user
if (user) {
var {encryptedData, iv} = req.body
var pc = new WXBizDataCrypt(config.appId, user.sessionKey)
var data = pc.decryptData(encryptedData, iv)
Object.assign(user, data)
return res.send({
code: 0
})
}
throw new Error('用户未登录')
})
彻底搞懂小程序登录流程-附小程序和服务端代码
用户登录是大部分完整 App 必备的流程
一个简单的用户系统需要关注至少这些层面
很多的业务需求都可以抽象成 Restful 接口配合 CRUD 操作
但登录流程却是错综复杂, 各个平台有各自的流程, 反倒成了项目中费时间的部分, 比如小程序的登录流程
对于一个从零开始的项目来说, 搞定登录流程, 就是一个好的开始, 一个好的开始, 就是成功的一半
本文就以微信小程序这个平台, 讲述一个完整的自定义用户登录流程, 一起来啃这块难啃的骨头
名词解释
先给登录流程时序图中出现的名词简单做一个解释
code
临时登录凭证, 有效期五分钟, 通过wx.login()
获取session_key
会话密钥, 服务端通过 code2Session 获取openId
用户在该小程序下的用户唯一标识, 永远不变, 服务端通过 code 获取unionId
用户在同一个微信开放平台帐号(公众号, 小程序, 网站, 移动应用)下的唯一标识, 永远不变appId
小程序唯一标识appSecret
小程序的 app secret, 可以和 code, appId 一起换取 session_key其他名词
rawData
不包括敏感信息的原始数据字符串,用于计算签名encryptedData
包含敏感信息的用户信息, 是加密的signature
用于校验用户信息是否无篡改iv
加密算法的初始向量小程序登录相关函数
wx.login
wx.getUserInfo
wx.checkSession
小程序的 promise
我们发现小程序的异步接口都是 success 和 fail 的回调, 很容易写出回调地狱
因此可以先简单实现一个 wx 异步函数转成 promise 的工具函数
这样我们就可以这样调用函数了
服务端实现
本 demo 的服务端实现基于 express.js
小程序登录
我们先实现一个基本的 oauth 授权登录
前端小程序登录
写在 app.js 中
服务端实现 oauth 授权
服务端实现上述
/oauth/login
这个接口获取用户信息
登录系统中都会有一个重要的功能: 获取用户信息, 我们称之为
getUserInfo
如果已登录用户调用 getUserInfo 则返回用户信息, 比如昵称, 头像等, 如果未登录则返回"用户未登录"
小程序的用户信息一般存储在
app.globalData.userInfo
中(模板如此)我们在服务端加上前置中间件, 通过 session 来获取对应的用户信息, 并放在 req 对象中
然后实现
/user/info
接口, 用来返回用户信息小程序调用用户信息接口
专为小程序发请求设计的库
小程序代码通过
http.get
,http.post
这样的 api 来发请求, 背后使用了一个请求库@chunpu/http 是一个专门为小程序设计的 http 请求库, 可以在小程序上像 axios 一样发请求, 支持拦截器等强大功能, 甚至比 axios 更顺手
初始化方法如下
具体使用方法可参照文档 https://github.com/chunpu/http#readme
自定义登录态持久化
浏览器有 cookie, 然而小程序没有 cookie, 那怎么模仿出像网页这样的登录态呢?
这里要用到小程序自己的持久化接口, 也就是 setStorage 和 getStorage
为了方便各端共用接口, 或者直接复用 web 接口, 我们自行实现一个简单的读 cookie 和种 cookie 的逻辑
先是要根依据返回的 http response headers 来种上 cookie, 此处我们用到了
@chunpu/http
中的 response 拦截器, 和 axios 用法一样当然我们还需要在发请求的时候带上所有 cookie, 此处用的是 request 拦截器
登录态的有效期
我们知道, 浏览器里面的登录态 cookie 是有失效时间的, 比如一天, 七天, 或者一个月
也许有朋友会提出疑问, 直接用 storage 的话, 小程序的登录态有效期怎么办?
问到点上了! 小程序已经帮我们实现好了 session 有效期的判断
wx.checkSession
它比 cookie 更智能, 官方文档描述如下
也就是说小程序还会帮我们自动 renew 咱们的登录态, 简直是人工智能 cookie, 点个赞👍
那具体在前端怎么操作呢? 代码写在 app.js 中
要注意, 这里的 session 不仅是前端的登录态, 也是后端 session_key 的有效期, 前端登录态失效了, 那后端也失效了需要更新 session_key
确保每个 Page 都能获取到 userInfo
如果在新建小程序项目中选择 建立普通快速启动模板
我们会得到一个可以直接运行的模板
点开代码一看, 大部分代码都在处理 userInfo....
注释里写着
但这样的模板并不科学, 这样仅仅是考虑了首页需要用户信息的情况, 如果扫码进入的页面也需要用户信息呢? 还有直接进入跳转的未支付页活动页等...
如果每个页面都这样判断一遍是否加载完用户信息, 代码显得过于冗余
此时我们想到了 jQuery 的 ready 函数
$(function)
, 只要 document ready 了, 就可以直接执行函数里面的代码, 如果 document 还没 ready, 就等到 ready 后执行代码就这个思路了! 我们把小程序的 App 当成网页的 document
我们的目标是可以这样在 Page 中不会出错的获取 userInfo
此处我们使用 min-ready 来实现此功能
代码实现依然写在 app.js 中
绑定用户信息和手机号
仅仅获取用户的 openId 是远远不够的, openId 只能标记用户, 连用户的昵称和头像都拿不到
如何获取这些用户信息然后存到后端数据库中呢?
我们在服务端实现这两个接口, 绑定用户信息, 绑定用户手机号
小程序个人中心 wxml 实现如下
小程序中的 bindUserInfo 和 bindPhoneNumber 函数, 根据微信最新的策略, 这俩操作都需要用户点击按钮统一授权才能触发
代码
本文所提到的代码都可以在我的 github 上找到
小程序代码在 wxapp-login-demo
服务端 Node.js 代码在 wxapp-login-server