微信小程序> 关于小程序鉴权那点事——oauth2.0

关于小程序鉴权那点事——oauth2.0

浏览量:772 时间: 来源:xERAths

刚接触小程序那会,一个接一个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情况。

后续有更新会及时补充。

前端小白,抛砖引玉...

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎