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

2023-11-12

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

后续有更新会及时补充。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于小程序鉴权那点事——oauth2.0 的相关文章

  • 深度学习实时表情识别

    背景 计算机动画代理和机器人为人机交互带来了新的维度 这使得计算机如何在日常活动中影响我们的社交生活变得至关重要 面对面的交流是一个以毫秒级的时间尺度运行的实时过程 这个时间尺度的不确定性是相当大的 这使得人类和机器有必要依赖感官丰富的感知
  • 超详细的R语言热图之complexheatmap系列(1)

    获取更多R语言和生信知识 请关注公众号 医学和生信笔记 公众号后台回复R语言 即可获得海量学习资料 目录 第一章 简介 1 1 设计理念 1 2 各章节速览 第二章 单个热图 2 1 颜色 2 2 行标题 列标题 2 3 聚类 2 3 1
  • 深度访谈:“告诉我,AI对企业到底有什么价值?”

    Eden是一家连锁经营企业的负责人 最近困扰他的是遍布全国直营和加盟店的数千名员工 如何在后疫情时代把企业的运营效率通过智能化提升一个层级 AskBot团队专注企业内部智能化 用AI去辅助人解决重复高频问题 因此才有了双方下面这一系列围绕企
  • matlab分频.m,分频器m是什么意思 音响分频器m. TW那个代表高音那个代表是低音?...

    音响分频器m TW那个代表高音那个代表是低音 T是treble 的缩写 指高音 M是mediant或middle的缩写 指中音 W是woof的缩写 指低音 音箱分频器m m 什么意思 音箱分频器m m 应该是接中音喇叭负 正两端 T T 接
  • 尚硅谷周阳老师 SpringCloud第二季学习笔记

    前言 首先感谢尚硅谷周阳老师的讲解 让我对springcloud有了很好的理解 周阳老师的讲课风格真的很喜欢 内容充实也很幽默 随口一说就是一个段子 我也算是周阳老师的忠实粉丝啦 先说说课程总体内容 以下是整理的笔记 SpringCloud
  • 带宽是什么

    带宽是什么 带宽 band width 又叫频宽 是指在固定的的时间可传输的资料数量 亦即在传输管道中可以传递数据的能力 在数字设备 中 频宽通常以bps表示 即每秒可传输之位数 在模拟设备中 频宽通常以每秒传送周期或赫兹 Hz 来表示 带

随机推荐

  • 超好用:免费的图床

    经常写文章的小伙伴可能会头疼 图片需要一张一张的上传 费劲也耗时 今天就推荐几款超简单的图床工具 图床就是一个在网络上存储图片的地方 目的是为了节省本地服务器空间 加快图片打开速度 话不多说 进入正题 非技术手段 1 SM MS 永久存储免
  • 根据任务需求自制数据集:Pytorch自定义数据集Dataset代码示例(有监督学习,输入输出均为图像)

    自定义数据集 一 使用torchvision io读取照片 二 使用PIL读取照片 一 使用torchvision io读取照片 import numpy as np import torch from PIL import Image i
  • eNSP 打开警告:请将eNSP相关应用程序添加到windows firewall的允许程序列表,并允许其在公用网络上运行!

    文章目录 1 警告截图 2 解决办法 1 警告截图 2 解决办法 思路 按照警告的提示信息 将 eNSP 相关应用添加到 windows firewall 防火墙 的允许程序列表 并允许其在公用网络上运行 此处以 Win 10 为例 找到对
  • yolov5训练PASCAL VOC数据集调试报错记录

    1 UserWarning torch meshgrid in an upcoming release it will be required to pass the indexing argument 解决方法 找到目录D Users J
  • h5开发网站-css实现页面的背景固定定位

    一 需求 在页面滚动时 背景图片保持不变 而不是跟随滚动 二 解决方式 使用背景固定定位 只需要在CSS中增加一个background attachment fixed 属性即可 具体代码 div class item right img
  • Merkle Patricia Tree (MPT) 以太坊merkle技术分析

    一 传统merkle树缺陷 我的这篇博客merkle tree 分析 详细解释了merkle树的原理和作用 然而传统的merkle树有他的局限性 以下对Vitalik blog原文的翻译可以很好的阐述 传统merkle树的一个特别的限制是
  • 创业公司如何提高管理能力?十条法则教你!

    1 明确的架构 接手任何一个部门的最重要的事情 是明确或者重新调整组织架构 架构的关键是 谁在什么位置 负责什么内容 一定要明确 所谓 明确 的意思是 不允许两个人交叉负责 也不允许集体领导 不允许有模糊的领域 出了问题 大家都清楚谁应该出
  • ubuntu的pycharm或程序卡主,强制关闭方法

    首先打开终端 输入 ps ef 查看正在运行的所有程序 找到想要关闭的程序如pycharm 3078和3192 输入 kill s 9 3078 kill s 9 3192 即可关闭pycharm 或者关闭别的程序
  • ssm整合redis

    1 配置文件 redis properties redis host 127 0 0 1 redis port 6379 redis password redis maxIdle 50 redis maxTotal 100 redis ma
  • ARCode和ARKit官网

    ARCode官网 https developers google cn ar ARCode支持机型 https developers google cn ar discover supported devices ARKit官网 https
  • Mysql中的not in和null

    给定一个表 tree id 是树节点的编号 p id 是它父节点的 id tree id p id 1 null 2 1 3 1 4 2 5 2 树中每个节点属于以下三种类型之一 叶子 如果这个节点没有任何孩子节点 根 如果这个节点是整棵树
  • OCR测试对比

    文字密度 测试一 测试二 测试三 测试四 颜色 测试一 测试二 测试三 测试四 阴影 测试一 测试二 测试三 测试四
  • Java调用python打包的程序.exe,包括获取exec()中打印的日志,亲测有效

    python写了一个方法 windows平台上怎么被Java服务调用呢 最简单的办法 python利用pyinstaller打包成 exe程序 Java程序通过Process调用 想同时获取 exe执行过程中打印出的日志用ProcessBu
  • Java网络编程之Socket编程

    Socket编程 下图是基于TCP协议的客户机与服务器通信逻辑 客户机使用Socket类型的套接字与服务器会话 服务器则需要使用ServerSocket类型的套接字侦听网络连接 再使用Socket类型的套接字与客户机会话 会话都是通过基于套
  • python3 ==和is的区别

    如图 详细说明了等号和is的区别 可以通过查看地址来解释报错的原因
  • 基于ZelinAI的周报小助手

    ZelinAI是一家领先的人工智能公司 专注于创新的AI解决方案 ZelinAI团队由科学家 工程师和领域专家组成 拥有丰富的经验和专业知识 提供自然语言处理 计算机视觉 数据分析和智能助理等领域的解决方案 帮助客户实现业务增长和效率提升
  • Nacos使用详解

    一 部署nacos 1 方式一 一般的windows和linux部署 需要的nacos server 1 4 1文件 https download csdn net download yueyue763184 87822434 spm 10
  • 入职外包一个月,我离职了。

    点击上方 程序员黑叔 选择 置顶或者星标 你的关注意义重大 前言 由于最近面试了几个人 印象最深刻的求职者是world 匿名 一个普普通通在深圳打拼的小年轻 来深圳两年 见过深圳粤海街道凌晨5点的夜晚 经历过公司从裁员到倒闭 为了生活 也去
  • 国际阿里云/腾讯云:定向流量包范围

    阿里系定向流量包规模 阿里系定向流量包一般都包括有手机天猫 手机淘宝 支付宝 优酷视频 钉钉 口碑 闲鱼 聚划算 蚂蚁财富等主流阿里系手机APP运用 但详细免流APP会依据不同的流量套餐以及业务协作状况发生变动 以流量包订购页面或许号卡处理
  • 关于小程序鉴权那点事——oauth2.0

    刚接触小程序那会 一个接一个web的方法 APi不支持 难受的只能敲着代码 流着眼泪 oauth2 0鉴权 一个access token 一个refresh token 一个expires in 在pc端我们可以使用cookie来轻松处理a