微信小程序登陆逻辑封装(附比较完整注释)

发布: 2019-07-13 16:05:56标签: 小程序

一、先看一下如何使用

  • login:登陆过返回用户信息,没有登陆过执行登陆逻辑
  • loginForce:强制登陆
01import {login, loginForce} from '@/utils/login'
02
03// 需要登陆才可以执行
04async function needLoginFn() {
05 await login()
06 // todo
07}
08
09// 强制登陆
10async function toRrefreshLogin() {
11 await loginForce()
12 // todo
13}
14
复制代码

二、完整微信小程序登陆逻辑

01import setting from '@/utils/setting' // 用户设置(具体代码在附录)
02import authority from '@/utils/authority' // localStorage用户信息保存(具体代码在附录)
03import request from '@/utils/request/doRequest' // 类似axios请求方法封装(代码略)
04import { redirectLogin } from '@/utils/router' // 重定向登陆,登陆后返回等逻辑(代码略)
05
06// 并发触发登陆,返回第一次登陆结果
07let prevCodeLogin
08
09
10/**
11 * @description: 检查是否有权限获取用户信息,有的话直接可以直接使用wx.getUserInfo
12 */
13async function validateSetting() {
14 const hasSetting = await setting.has('userInfo')
15 if (!hasSetting) {
16 // 没有权限的话,清空保存用户信息,重定向登陆
17 authority.clear()
18 redirectLogin()
19 }
20 return hasSetting || handleError(new Error('没有获取UserInfo权限'))
21}
22
23/**
24 * @description: 检查sessionKey是否过期,过期的话需要重新wx.login获取code登陆
25 */
26export function validateSession() {
27 return new Promise(resolve => {
28 wx.checkSession({
29 success() {
30 resolve(1)
31 },
32 fail() {
33 resolve(0)
34 }
35 })
36 })
37}
38
39/**
40 * @description: 通过code登录
41 * 用户登陆过:后台可以获取openid或者unionId直接查询到用户信息,实现静默登陆
42 * 用户第一次:需要重定向授权页面,点击getUserInfo的按钮授权登陆
43 */
44export async function codeLogin(force) { // force:是否强制登陆
45 // 获取缓存中的用户信息,有id表示已经登陆过
46 const cacheUser = authority.get() || {}
47 if (!force && cacheUser.id) return cacheUser
48
49 // isValid:后台code换的session_key是否过期,过期的话不可以解处加密字符串中的用户信息,需要重新登陆
50 const isValid = await validateSession()
51 if (force || !isValid) {
52 if (prevCodeLogin) return prevCodeLogin
53 prevCodeLogin = new Promise((resolve, reject) => {
54 wx.login({
55 async success({ code }) {
56 try {
57 // 将code发送给后台,后台可以获取到openid,session_key等
58 const res = await request({
59 url: '/api/client/v1/auth/session',
60 method: 'post',
61 data: { code }
62 })
63 // code获取后台返回的用户信息(登陆过返回完整用户信息,没有登陆过返回token,token用户后台关联当前用户的session_key)
64 const { user = {}, token } = res
65 user.token = token
66 authority.set(user)
67 prevCodeLogin = null
68 return resolve(user)
69 } catch (e) {
70 reject(e)
71 }
72 },
73 fail(e) {
74 prevCodeLogin = null
75 reject(e)
76 }
77 })
78 })
79
80 return prevCodeLogin
81 }
82 return cache
83}
84
85/**
86 * @description: 解密用户信息或者手机号(encrypted_data, iv)
87 * encrypted_data, iv:有获取用户信息权限可以通过getUserInfo获取,否则通过授权按钮获取
88 */
89export async function toDecode({ encrypted_data, iv }) {
90 // 此方法header中默认携带了code登陆返回的token,后台通过token查到session_key,然后解密出用户信息
91 const { user, token } = await request({
92 url: '/api/client/v1/auth/login',
93 method: 'post',
94 data: {
95 encrypted_data,
96 iv
97 }
98 })
99 if (user) {
100 user.token = token
101 authority.set(user)
102 return user
103 } else {
104 return Promise.reject(new Error('登录decode失败'))
105 }
106}
107
108/**
109 * @description: getUserInfo 获取加密字符串,传给后台解密出用户信息
110 */
111export function decodeUserInfo() {
112 return new Promise(resolve => {
113 wx.getUserInfo({
114 lang: 'zh_CN',
115 async success(res) {
116 const { encryptedData, iv } = res
117 const user = await toDecode({
118 encrypted_data: encryptedData,
119 iv
120 })
121 resolve(user)
122 }
123 })
124 })
125}
126
127/**
128 * @description: 登陆封装
129 */
130export async function login() {
131 // 通过wx.login获取code发给后台
132 const user = await codeLogin()
133 // 返回id表示静默登陆成功
134 if (user.id) {
135 return user
136 }
137 // 判断是否有获取用户信息权限,没有的重定向登陆
138 await validateSetting()
139 // 有getUserInfo权限才会走下边代码,不需要授权直接使用getUserInfo登陆
140 return decodeUserInfo()
141}
142
143/**
144 * @description: 强制登录
145 */
146export async function loginForce() {
147 authority.clear()
148 await login()
149}
复制代码

三、附录:所用工具类

authority:简单封装localStorage

01// 储存前缀
02const key = 'prefix_key'
03
04// 储存有效时间
05const maxAge = 1000 * 60 * 60 * 24 * 60
06
07export default {
08 get() {
09 try {
10 const user = wx.getStorageSync(key)
11 if (!user || user.time + maxAge < new Date().getTime()) return {}
12 return user || {}
13 } catch (e) {
14 return {}
15 }
16 },
17 set(user) {
18 if (!user) return null
19 user.time = new Date().getTime()
20 const oldUser = this.get() || {}
21 const newUser = { ...oldUser, ...user }
22 wx.setStorageSync(key, newUser)
23 return newUser
24 },
25 clear() {
26 const user = this.get() || {}
27 wx.clearStorageSync()
28 wx.removeStorageSync(key)
29 this.set({ user: {} })
30 return user
31 }
32}
33
34
复制代码

setting:用户设置简单封装

01const noop = () => {}
02
03// 并发查询setting返回同一个结果
04let preGetSetting
05
06// 需要直接点击,不能在异步函数之中(可以使用wx.showModal的success中使用)
07export function openSetting(name, callback = noop) {
08 wx.getSetting({
09 success({ authSetting }) {
10 const key = `scope.${name}`
11 let success = false
12 if (authSetting[key] === false) {
13 wx.show
14 wx.openSetting({
15 success({ authSetting: auth }) {
16 if (callback) {
17 success = auth[key] !== false
18 callback(success)
19 }
20 }
21 })
22 } else {
23 // 已经有权限
24 if (typeof callback === 'function') {
25 callback(success)
26 }
27 }
28 }
29 })
30}
31
32// 获取用户授权信息
33export function getSetting() {
34 if (preGetSetting) return preGetSetting
35 preGetSetting = new Promise(resolve => {
36 wx.getSetting({
37 success({ authSetting }) {
38 preGetSetting = null
39 resolve(authSetting)
40 },
41 fail(e) {
42 preGetSetting = null
43 reject(e)
44 }
45 })
46 })
47 return preGetSetting
48}
49
50async function hasSetting(name) {
51 const setting = await getSetting()
52 const key = `scope.${name}` // userLocation、userInfo
53 return setting[key]
54}
55
56export default {
57 open: openSetting,
58 get: getSetting,
59 has: hasSetting
60}
61
复制代码