小程序的登陆流程不同于网页端的登陆,步骤多较繁琐,这里主要讨论前端的具体实现。
一. 登陆流程详解
- 这个时序图信息量很多,我们把它拆解开来,代码上纵向我们只用关心“前端”,之后重点关心红框里的第一步和第二步,从简单入手。
1. 第一步:code换自定义登陆态
前端:
- 使用微信api wx.login() 获取小程序的登录凭证code
- 通过接口向后端发送code换取自定义登录态比如token,jwt等。
- 如果需要统一微信平台下的身份,如微信公众号,或PC端的微信登陆用户,一般在登陆的同时会调用 wx.getUserInfo()获取到加密信息,再将加密数据通过后端接口返回给后端,之后拿到用户的openId或是unionId,确认用户身份。
提一下后端工作:
- 拿到code,结合小程序 appId、小程序 appSecret向微信接口服务(微信服务器)发请求,拿到的返回值中有openId和session_key等。这两项,一项是用户唯一标识,一项是会话密钥(用于解密微信的加密数据如手机号等,不是永久有效,所以称之为会话密钥,前端可用wx.checkSession()检测)
- 拿到加密数据,使用上一步保存的session_key进行解密,之后得到有用的开放数据,进行数据储存或返回前端展示。
2. 第二步:储存自定义登陆态
- 使用小程序的数据缓存,保存自定义登录态。
3. 附加的第三步
- 自定义封装wx.request(),发起业务时带上自定义登陆态。
- 在小程序生命周期onLuanch中加上登陆判定,如果登陆过就继续,如果没有登陆过则进行登陆操作等。
二. 上代码
- 笔者单独将登陆作为一个页面,如若无此需求,也可将代码拆分。
login.wxml
- 笔者的小程序是必须要获取用户信息的,所以直接在button授权获取用户信息的触发事件里,写登陆逻辑。
非必要第一时间获取用户信息的小程序可直接在其他地方写登陆逻辑。
<!--login.wxml片段-->
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo"></button>
login.js
- showToast ():用wx.showToast()封装的函数,用于提示反馈
- request():用wx.request()封装的基于promise的函数,可统一处理错误、带上登录态、权限判定等等。
Page({
getUserInfo() {
showToast('登陆中...', 'loading', true)
wx.login({
success: ({ code }) => {
// 1. 拿到code发送给后端
request({
url: '/wechat/mini/token',
data: { code }
}).then(({ data: { result } }) => {
// 2. 将token储存
wx.setStorageSync('token', result.token)
wx.getUserInfo({
success: data => {
// 3. 将加密用户信息发送至后端解密
request({
url: '/wechat/mini/userinfo',
data: {
token: result.token,
payload: data
},
method: 'POST'
}).then(({ data: { result } }) => {
wx.showToast({ title: '登陆成功', icon: 'success' })
// 保存用户信息
wx.setStorageSync('userinfo', result)
// 跳转页面...
})
},
fail: () => {
showToast('请授权用户信息后重试!')
}
})
})
},
fail() {
showToast('网络状况不佳,请检查网络后重试!')
}
})
}
})
request.js
使用wx.request()封装的基于Promise的函数。
const request = ({ url, data = {}, method = 'GET' }) => {
return new Promise((resolve, reject) => {
wx.request({
url,
data,
method,
// 一种做法,在header中带上登录态,方式取决于和后端的约定
header: {
Authorization: wx.getStorageSync('token')
},
success: res => {
if (res.statusCode === 200) {
resolve(res)
} else {
// 自定义错误处理
reject()
if (res.statusCode === 403) {
...
} else if (res.statusCode === 401) {
...
}
}
},
fail: () => {
reject()
showToast('请求失败,请检查网络后重试!')
}
})
})
}
export default request
app.js
如果是必要登录的小程序,可在app.js中去判断登录态和登陆态时效,以跳转登陆。
App({
onLaunch() {
const token = wx.getStorageSync('token')
if (!token) {
wx.reLaunch({ url: '/pages/login/login' })
}
}
})
三. 扩展提升:小朋友,你是不是有很多的问号?
1. 为什么要用wx.login()微信api,为什么不能用户名密码登陆?
- 小程序运行在微信客户端内部,遵循微信的机制就可以获取到用户的很多信息,比如昵称、头像url、性别、省份、城市、国家,以及加密信息openId(同一应用下唯一标识)、unionId(微信平台唯一标识)、手机号、微信号等等。使用wx.login()获取code是拿到这一系列信息的第一步。
2. 用户唯一标识为什么不能用微信号,openId和unionId是什么?
- 微信号在第一次注册微信之后会有一次修改机会,所以不能用微信号作为用户唯一标识。
- openId是用户在同一应用下的唯一标识,换句话说,就是一个用户登陆了小程序,之后用户改了微信号再次登陆或者是其他操作,用户的openId是不变的,可以用来当作唯一标识。
- unionId用微信的原话解释:只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionId是唯一的。
- ps:如果未涉及到微信开放平台的用户同一,即如果只是单一应用,就不用管unionId。
3. 如何判断登陆态过期?
- 后端向前端签发的登录态一般是存在过期时间的。
- 小程序没有浏览器的cookie来进行定时储存。
- 提供一种解决方法,可以让后端在签发登录态时,顺便加上登录态过期时间戳,这样每次打开小程序的onLaunch的时候进行判断,如果快要过期则重新请求刷新登录态。
- 当然也可能或许大概存在用户连续使用小程序24小时或更久不关闭,导致登录态直接过期的情况,针对这种情况,就要特殊处理了,这里不再赘述。
4. 为什么不使用bindgetuserinfo回调的detail来获取用户信息?
- 拿到用户信息必须使用button来触发授权,用户在点击授权之后,一可以从button绑定的bindgetuserinfo回调事件中的e.detail.userInfo拿到,二在授权之后就可以用wx.getUserInfo()随时获取到用户信息。
- 如果不需要解密数据,以上两种二选一。
- 注意:当需要后端解密的时候,顺序很关键,必须要先调用wx.login()拿到code,等同于后端先拿到session_key,再将加密数据给后端解密才能成功。
- 代码里先触发了bindgetuserinfo,如果用回调的detail解密,就等于wx.login()在后,顺序颠倒,则解密失败。所以在这里额外用wx.getUserInfo()来获取用户信息。
博主初来乍到,文笔不加,如有错误和疑问,欢迎留言加关注,共同学习进步!