- 公众平台/小程序服务端API的access_token的内部设计
一、背景 对于使用过公众平台的API功能的开发者来说,access_token绝对不会陌生,它就像一个打开家门的钥匙,只要拿着它,就能使用公众平台绝大部分的API功能。因此,对于开发者而言,access_token的使用方式就变得尤其的重要。在日常API接口的运营中,经常遇到各种的疑问:为什么我的access_token突然非法了?为什么刚刚拿到的access_token,用了10min就过期了?对于这些疑问,我们提供出access_token的设计方案,便于开发者对access_token使用方式上的理解。 对于access_token的获取,可以参考公众平台的官方文档:auth.getAccessToken、获取Access token 二、access_token的内部设计 2.1 access_token的时效性 众所周知,access_token是通过appid和appsecret来生成的。内部设计的步骤如下: (1)开发者通过https请求方式: GET https://API.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,传入appid及apppsecret的参数 (2)公众平台后台会校验appid和哈希(appsecret)是否与存储匹配,若匹配,结合当前时间戳,生成新的access_token。 (3)生成新的access_token的同时,会对老的access_token的过期时间戳更新为当前时间戳。 (4)返回新的access_token给开发者。 这里以图示的方式说明一下,新旧token交替过程: [图片] 从上图需要注意的几点: (1)公众平台存储层只会存储新老两个access_token,意味着假设开发者重复调用3次接口,则会导致最早的access_token立刻失效。 (2)虽然请求新的access_token后,老的access_token过期时间会更新为当前时间,但也不会立刻失效,原理请参考 【2.2 access_token 的逐渐失效性】 (3)出于信息安全考虑,公众平台并不会明文存储appsecret,仅存储appid以及appsecret的哈希值。因此开发者要妥善保管appsecret。当appsecret疑似泄露时,需要及时登录mp.weixin.qq.com重置appsecret。 2.2 access_token 的逐渐失效性 从【access_token的时效性】了解到,当开发者请求获取新的access_token时,老的access_token过期时间会被更新为当前时间,但此时不会立刻失效,因为公众平台会提供【5分钟的新老access_token交替缓冲时间】,因此也称为access_token 的逐渐失效性。 实现的原理是: 1. 由于老的access_token过期时间戳已被刷新,所以在API接口请求期间,带上的access_token解开后,过期时间戳会加上5分钟,然后和当前设备时间进行比对,若超过当前设备时间,判断为失效。 2. 公众平台的设备会保持时钟同步,但设备之间仍然可能会存在1-2分钟的时间差异,所以【5分钟】并非绝对的时间值。当开发者获取到新的access_token后应该尽快切换到新的access_token。 [图片] 从上图需要注意的几点: (1)由于存在设备时间同步的差异,可能会导致开发者遇到拿着老的access_token请求API接口,部分请求成功,部分请求失败的情况,建议开发者获取到新的access_token后尽快使用。 (2)通过理解两个图示,对开发者来说,access_token是相当关键且不能乱调的接口,建议开发者统一管理access_token,以免造成多次请求导致access_token失效。
2021-05-11 - 记录几种特殊场景进入小程序
一、小程序打开另一个小程序 navigator组件 和 wx.navigateToMiniProgram API 这个文档写的挺详细的就不多说了。 https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html https://developers.weixin.qq.com/miniprogram/dev/api/navigate/wx.navigateToMiniProgram.html 二、扫描普通二维码打开小程序 1、二维码链接配置 首先配置普通二维码地址:小程序管理后台--开发管理--开发设置--扫普通链接二维码打开小程序,配置好二维码规则之后,使用符合规则的链接生成普通二维码,扫码就可进入对应页面。测试的时候需要把完整二维码地址添加到测试链接处保存配置,如果二维码链接符合规则但是没有配置到管理后台,扫码之后就会进入正式版对应页面。 2、二维码内容获取 在小程序后台配置二维码跳转小程序规则之后即可使用微信(6.5.6及其以上客户端版本)扫码打开小程序。二维码链接内容会以参数 q 的形式带给页面,在onLoad事件中提取 q 参数并自行 decodeURIComponent 一次,即可获取原二维码的完整内容。 Page({ onLoad(query) { const q = decodeURIComponent(query.q) // 获取到二维码原始链接内容 const scancode_time = parseInt(query.scancode_time) // 获取用户扫码时间 UNIX 时间戳 } }) 参考文档https://developers.weixin.qq.com/miniprogram/introduction/qrcode.html 三、服务号绑定“JS接口安全域名”下的网页跳转小程序 微信开放标签 wx-open-launch-weapp 注意:微信版本要求为:7.0.12及以上,系统版本要求为:iOS 10.3及以上、Android 5.0及以上 接收参数:在页面onLoad周期函数获取 a = options.a, b=options.b 参考文档https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_Open_Tag.html#21 四、短信、邮件、外部网页、微信内等拉起小程序 1)无参数或者参数固定 打开小程序管理后台--右上角处工具--生成URL Scheme,按要求选择填写即可,复制生成的URL Scheme,如下示例: location.href = 'weixin://dl/business/?t= *TICKET*' 该跳转方法可以在用户打开 H5 时立即调用,也可以在用户触发事件后调用。 [图片] 也可通过调用服务端接口生成,感觉完全没必要那么麻烦,不推荐! 2)动态参数 通过调用服务端接口生成,文档https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/url-scheme/urlscheme.generate.html根据文档调用就行,比较详细了。 请求示例: { "jump_wxa": { "path": "/pages/publishHomework/publishHomework", "query": "a=123&b=321" }, "is_expire":true, "expire_time":1606737600 } 这里主要说一下,接收参数,文档没看到,亲测:在页面onLoad周期函数获取 a = options.a, b=options.b URL scheme和URL Link(https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-link/generateUrlLink.html)使用基本差不多,就不单独记录了。 五、微信内拉起小程序(电商类目) 这个由于本人没有电商的账号没有亲自使用过,所以看文档吧.......先记录有这么个途径。 文档地址:https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/short-link/generateShortLink.html
2022-08-29 - h5页面打开app,微信开放标签的使用教程。
<wx-open-launch-app id=“launch-btn-2” appid=“您的appid”> <template> <style>.btn { padding: 12px }</style> <button class=“btn”>App内查看</button> </template> </wx-open-launch-app> [代码]<script> let url = window.location.href; if(url.indexOf("#")>0){ url = url.split('#')[0]; } $.ajax({ method: "get", url: '获取tocken等url,是后台人员提供的', data: { }, contentType: false, // 告诉jQuery不要去设置Content-Type请求头 processData: false, // 告诉jQuery不要去处理发送的数据 }) .done(function( res ) { console.log(res); if(res.code==1){ var res_data = res.data; console.log(res_data); wx.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: res_data.appId, // 必填,公众号的唯一标识 timestamp: res_data.timestamp, // 必填,生成签名的时间戳 nonceStr: res_data.nonceStr, // 必填,生成签名的随机串 signature: res_data.signature,// 必填,签名 jsApiList: [ "menuItem:share:facebook" ], // 必填,需要使用的JS接口列表 openTagList: ['wx-open-launch-app'] // 可选,需要使用的开放标签列表,例如['wx-open-launch-app'] }); }else{ // _this.$toast("请用在微信中打开"); alert(res+"123"); } }); wx.ready(function () { wx.checkJsApi({ jsApiList: ['wx-open-launch-app'], // 需要检测的JS接口列表,所有JS接口列表见附录2, success: function (res) { console.log('可用') }, fail: (err) => { console.log(err, '不可用') } }); var btn = document.getElementById('launch-btn-2'); btn.addEventListener('ready', function (e) { console.log('ready'); }); //点击立即使用按钮 btn.addEventListener('launch', (e)=> { console.log('launch'); }); btn.addEventListener('error', function (e) { console.log('error'); }); // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中 }); //如果是vue,上面的标签部分,则可以用这个: <wx-open-launch-app id="launch-btn" appid="您的appid"> <script type="text/wxtag-template"> <style> .bottom-btn{ -webkit-user-select:none; font-weight:bold; } .bottom-btn:link,.bottom-btn:visited{color:#fff;} .bottom-btn:active{color:#fff; background:linear-gradient(90deg,rgba(255,82,59,1) 0%,rgba(255,176,69,1) 100%);} </style> <a class="bottom-btn">立即使用</a> </script> </wx-open-launch-app> 如果有问题,可联系我:13241108634,加微信即可。[代码]
2021-03-01 - 微信小程序调用oss上传至阿里云
1.首先创建oss密钥 这里我新建了一个名字 命名为config.js var fileHost = "https://*****************/"; //你的阿里云地址最后面跟上一个/ 在你当前小程序的后台的uploadFile 合法域名也要配上这个域名 var config = { //aliyun OSS config uploadImageUrl: `${fileHost}`, // 默认存在根目录,可根据需求改 AccessKeySecret: '*******', // AccessKeySecret 去你的阿里云上控制台上找 OSSAccessKeyId: '********', // AccessKeyId 去你的阿里云上控制台上找 timeout: 87600 //这个是上传文件时Policy的失效时间 }; module.exports = config 2.创建上传配置文件 我这里命名为upload.js const env = require('config.js'); //配置文件,在这文件里配置你的OSS keyId和KeySecret,timeout:87600; // 以下算法在https://gitee.com/chenkuo1997/oss-wx.git 复制到同upload.js目录下即可 const base64 = require('base64.js');//Base64,hmac,sha1,crypto相关算法 require('hmac.js'); require('sha1.js'); const Crypto = require('crypto.js'); /* *上传文件到阿里云oss *@param - filePath :图片的本地资源路径 *@param - dir:表示要传到哪个目录下 *@param - successc:成功回调 *@param - failc:失败回调 */ const uploadFile = function (filePath, dir, successc, failc) { if (!filePath || filePath.length < 9) { wx.showModal({ title: '图片错误', content: '请重试', showCancel: false, }) return; } console.log('上传图片.....'); //图片名字 可以自行定义, 这里是采用当前的时间戳 + 150内的随机数来给图片命名的 console.log(dir) const aliyunFileKey = dir+ new Date().getTime() + Math.floor(Math.random() * 150) + '.png'; const aliyunServerURL = env.uploadImageUrl;//OSS地址,需要https const accessid = env.OSSAccessKeyId; const policyBase64 = getPolicyBase64(); const signature = getSignature(policyBase64);//获取签名 console.log(env) wx.uploadFile({ url: aliyunServerURL,//开发者服务器 url filePath: filePath,//要上传文件资源的路径 name: 'file',//必须填file formData: { 'key': aliyunFileKey, 'policy': policyBase64, 'OSSAccessKeyId': accessid, 'signature': signature, 'success_action_status': '200', }, success: function (res) { console.log(res) if (res.statusCode != 200) { failc(new Error('上传错误:' + JSON.stringify(res))) return; } successc(aliyunServerURL+aliyunFileKey); }, fail: function (err) { err.wxaddinfo = aliyunServerURL; failc(err); }, }) } const getPolicyBase64 = function () { let date = new Date(); date.setHours(date.getHours() + env.timeout); let srcT = date.toISOString(); const policyText = { "expiration": srcT, //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了 "conditions": [ ["content-length-range", 0, 5 * 1024 * 1024] // 设置上传文件的大小限制,5mb ] }; const policyBase64 = base64.encode(JSON.stringify(policyText)); return policyBase64; } const getSignature = function (policyBase64) { const accesskey = env.AccessKeySecret; const bytes = Crypto.HMAC(Crypto.SHA1, policyBase64, accesskey, { asBytes: true }); const signature = Crypto.util.bytesToBase64(bytes); return signature; } module.exports = uploadFile; 3.小程序页面调用的时候 const uploadImage = require('../../utils/upload'); /** * 上传图片 */ uploadImage() { let that = this; let applyRefundImgList = that.data.applyRefundImgList wx.chooseImage({ count: 5 - applyRefundImgList.length, //处理图片上传数量 (可根据自身需求配置) success: function(res) { wx.showLoading({ title: '上传中', mask: true }) for (let index = 0; index < res.tempFilePaths.length; index++) { //applyrefund 则为bucket下的文件夹路径 可根据自身需求进行分类 uploadImage(res.tempFilePaths[index], `applyrefund/${shopUuid}/`, function(res) { wx.hideLoading() applyRefundImgList.push(res) that.setData({ applyRefundImgList }) }, function(res) { wx.hideLoading() } ) } } }) },
2021-07-06