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

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

后续有更新会及时补充。

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

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 单点登录结合 OAuth2.0 是一种常见的身份验证和授机制。OAuth2.0 允许用户使用他们在其他应用程序中拥有的凭据来登录您的应用程序,而无需创建新的用户名和密码。 在 Vue 中实现单点登录结合 OAuth2.0 的步骤如下: 1. 注册 OAuth2.0 提供商:您需要选择一个 OAuth***获取客户端ID和客户端密钥。 2. 安装依赖:使用 npm 或 yarn 安装用于处理 OAuth2.0 的库,例如 `vue-oauth2-oidc`。 3. 配置 OAuth2.0 客户端:在 Vue 项目的配置文件中,将 OAuth2.0 客户端的配置信息(客户端ID、客户端密钥、授终结点等)添加进去。 4. 实现登录和授流程:在 Vue 组件中,您可以创建一个登录按钮或链接,当用户点击时触发 OAuth2.0 的登录流程。您可以使用 `vue-oauth2-oidc` 提供的方法来处理认证请求和获取访问令牌。 5. 处理回调:当用户成功登录并授后,OAuth2.0 提供商将重定向用户到您的应用程序,并附带一个授码。您需要在 Vue 组件中处理回调,验证授码并交换访问令牌。 6. 保持登录状态:一旦您获得访问令牌,您可以将其存储在 Vue 的状态管理器(如Vuex)中,以便在应用程序的其他部分使用。您可以使用访问令牌来获取用户信息或调用受保护的 API。 通过以上步骤,您可以实现 Vue 单点登录结合 OAuth2.0。请注意,具体的实现细节可能因您选择的 OAuth2.0 提供商而有所不同,您需要查阅相关文档和示例代码来完成集成。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值