刚接触小程序那会,一个接一个web的方法、APi不支持,难受的只能敲着代码,流着眼泪。
oauth2.0鉴权,一个access_token,一个refresh_token,一个expires_in。
在pc端我们可以使用cookie来轻松处理access_token过期,自动刷token。
然而我们在小程序中是不支持cookie的,我们只能通过缓存机制来保存token,这个时候是不能监听到token是否过期,只有通过接口调用的返回结果才知道token是否过期。
1.Token1.0
记得第一次为了赶时间,就急急忙忙写了个请求方法:
getAccessToken(callback) { let that = this console.log('→获取token') var date = new Date(); var dt = date.getTime(); var dd = 0; var expires_in = wx.getStorageSync('expires_in'); if (dt = expires_in || isNaN(expires_in)) { if (wx.getStorageSync('access_token') != 'wait') { wx.setStorageSync('access_token', 'wait') console.log('→token过期,刷新token') var refresh_token = wx.getStorageSync('refresh_token'); wx.request({ url: API.refreshToken + refresh_token, method: 'POST', header: { 'Content-Type': 'application/json;charset=UTF-8', "Authorization": "Basic bGl6LXJlZHBhY2thZ2Utd3g6c2VjcmV0" //base64加密liz-youli-wx:secret }, success: function(res) { util.waitHide(); if (res.data.error == 'invalid_grant' || res.data.error == 'invalid_token') { console.log('→refresh_token失效') var unionid = wx.getStorageSync('unionid', unionid); //unionid // that.getToken(API.getToken + 'unionid_' + unionid + '_type_2'); //重新拿token let url=API.getToken + 'unionid_' + unionid + '_type_2' util.waitShow(); let that = this console.log('→项目开始获取token开始/n', url); wx.request({ url: url, method: 'POST', data: {}, header: { 'Content-Type': 'application/json;charset=UTF-8', "Authorization": "Basic bGl6LWxpbWEtd3g6c2VjcmV0" //base64加密liz-youli-wx:secret }, success(res) { util.waitHide(); console.log(res, '→数据') util.delayed30s(res.data.access_token, res.data.refresh_token, res.data.expires_in); callback(res.data.access_token); console.log('→项目开始获取token结束', url); }, fail(e) { console.log(e, '→获取token失败'); } }); } else { console.log('→刷新token成功') util.delayed30s(res.data.access_token, res.data.refresh_token, res.data.expires_in); callback(wx.getStorageSync('access_token')); } }, fail: function(e) { util.waitHide(); } }) } else { setTimeout(() = { callback(wx.getStorageSync('access_token')); }, 2000) } } else { console.log('→token未过期'); callback(wx.getStorageSync('access_token')); } }, fetchToken(url, method, data, callback, hideLoading) { // util.waitShow(); wx.showNavigationBarLoading() if (hideLoading == '1') { console.log('hideLoading:' + hideLoading) // util.waitHide(); wx.hideNavigationBarLoading() } let self = this self.getAccessToken((res) = { console.log(res, "→获取token成功") wx.request({ url: url, method: method, data: data, header: { 'Content-Type': 'application/json;charset=UTF-8', "Authorization": "bearer " + res }, success(res) { // util.waitHide(); wx.hideNavigationBarLoading() console.log('→返回数据', res.data); if (res.data.error == 'invalid_token') { console.log('invalid_token') var unionid = wx.getStorageSync('unionid', unionid); //unionid self.getToken(API.getToken + 'unionid_' + unionid + '_type_2'); callback(null, res.data); } else { callback(null, res.data); } console.log('→请求结束', url, data); }, fail(e) { console.log(e) // util.waitHide(); wx.hideNavigationBarLoading() util.showModal('加载失败', '网络不好,稍后再试试咯~') callback(e); } }); }) }虽然这个已经满足基本开发要求。也摆脱了在token过期时,同时几个请求发出时token刷新几次的情况,但是还是感觉会有事故发生。
于是就想着封装一下token的存储、删除、刷新及获取。
2.Token2.0
通过promise来进行第一次封装,也在线上版本使用,效果看起来还不错,具体代码是这样的:
/** * AuthProvider.js */const wxRequest = require('./wxRequest')const API = require('./api')const Promise = require('./es6-promise');function onLogin() { let url = API.getToken + 'unionid_' + wx.getStorageSync('unionid') + '_type_2'; let token = API.SECRET; return wxRequest.fetch(url, { type: 'Basic', value: token }, '', "POST").then((res) = { saveTokens(res.data.access_token, res.data.refresh_token, res.data.expires_in); return res.data.access_token }).catch((req) = { return 'error' })}/*刷新时删除已有的access_token防止再次刷新*/function setWait() { wx.removeStorageSync('access_token');}function saveTokens(access_token, refresh_token, expires_in) { wx.setStorageSync('access_token', access_token); wx.setStorageSync('refresh_token', refresh_token); var exp = new Date(); var expires_ins = exp.getTime() + expires_in * 1000 - 30000; wx.setStorageSync('expires_in', expires_ins);}function onRefreshToken() { setWait(); let token = API.SECRET; var url = API.refreshToken + wx.getStorageSync('refresh_token'); return wxRequest.fetch(url, { type: 'Basic', value: token }, '', 'POST').then((res) = { if (res.data.access_token) { saveTokens(res.data.access_token, res.data.refresh_token, res.data.expires_in); return res.data.access_token; } else { return onLogin().then(res = { return res }); } }).catch(req = { if (wx.getStorageSync('refresh_token') != null) { return onLogin().then(res = { return res }); } })}function getAccessToken() { var date = new Date(); var dt = date.getTime(); var expires_in = wx.getStorageSync('expires_in'); if ((!expires_in || dt = expires_in) && wx.getStorageSync('access_token')) { return onRefreshToken(); } else if (!wx.getStorageSync('access_token')) { return new Promise((resolve, reject) = { setTimeout(() = { resolve(wx.getStorageSync('access_token')) }, 2000) }) } else { return new Promise((resolve, reject) = { resolve(wx.getStorageSync('access_token')) }) }}module.exports = { onLogin: onLogin, getAccessToken: getAccessToken}token单独封装出来使用,借助promise的异步处理,是处理思路明确了一点。有一点不友好的是,引入了一个es6_promise包,增加了小程序整体的大小,这个虽然说是无可厚非的,但是为了token的使用就去用一个包很不划算。
于是又开始捯饬了些时间:
3.Token3.0
通过事件订阅的方式去做了token的封装,用设计模式去处理token的发布:
手写了一个小小的发布订阅方法,使得代码量又减少了一点。
/** * MessageCenter.js */function MessageCenter() { let message = {}; this.register = function (messageType) { if (typeof message[messageType] == 'undefined') { message[messageType] = []; } else { console.log("消息已被注册"); } } this.subscribe = function (messageType, func) { if (typeof message[messageType] != 'undefined') { message[messageType].push(func); } else { console.log("消息未注册,不能进行订阅"); } } this.fire = function (messageType, args) { if (typeof message[messageType] == 'undefined') { console.log("消息未注册,不能进行订阅"); return false; } let events = { type: messageType, args: args || {} } message[messageType].forEach(function (item) { item(events); }) }}module.exports={ MessageCenter:MessageCenter}/** *AuthProvider.js */import {MessageCenter} from './MessageCenter';import {getToken, refreshToken} from './token';const API = require('./config');const Promise = require('./es6-promise');let message = new MessageCenter();message.register('token');let params = { method: 'POST', token: { type: 'Basic', value: API.SECRET }}function onLogin() { params.unionId = wx.getStorageSync('unionid'); return getToken(params).then((res) = { saveTokens(res.access_token, res.refresh_token, res.expires_in); message.fire('token', res.access_token); return res.access_token }).catch((req) = { return 'error' })}function setWait() { wx.removeStorageSync('access_token');}function saveTokens(access_token, refresh_token, expires_in) { wx.setStorageSync('access_token', access_token); wx.setStorageSync('refresh_token', refresh_token); let exp = Date.now(); let expires_ins = exp + expires_in * 1000 - 30000; wx.setStorageSync('expires_in', expires_ins);}function onRefreshToken() { setWait(); params.refresh_token = wx.getStorageSync('refresh_token'); return refreshToken(params).then((res) = { if (res.access_token) { saveTokens(res.access_token, res.refresh_token, res.expires_in); message.fire('token', res.access_token); return res.access_token; } else { return onLogin().then(res = { return res }); } }).catch(req = { if (wx.getStorageSync('refresh_token') != null) { return onLogin().then(res = { return res }); } })}function getAccessToken() { let date = Date.now(); let expires_in = wx.getStorageSync('expires_in'); if ((!expires_in || date = expires_in) && wx.getStorageSync('access_token')) { return onRefreshToken() } else if ((!expires_in || date = expires_in) && !wx.getStorageSync('access_token')) { return new Promise((resolve, reject) = { message.subscribe("token", (event) = { resolve(event.args) }); }) } else { return new Promise((resolve, reject) = { resolve(wx.getStorageSync('access_token')) }) }}module.exports = { onLogin: onLogin, getAccessToken: getAccessToken}当然中间调试的版本有很多啦,因为开发的小程序很多,目前线上使用的就这三个,看起来效果都还可以,没有出现同时多次刷新token情况。
后续有更新会及时补充。
前端小白,抛砖引玉...













