记录--微信调用jssdk全流程详解
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
微信调用jssdk全流程详解
系统框架使用的是前后端分离,前端使用vant,后端是springboot
一、网页授权的时序图
二、公众号配置
1. 绑定域名
登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。也就是这样:
点击设置之后,弹出这样一个输入框,输入服务器所在的域名:
2:引入js文件
直接在你的页面里引入js文件就行
1 | < script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></ script > |
三、前端方法
1. 初始化方法,从后台获取基本的参数
前端进入需要扫一扫功能的页面时候,在mounted方法里面,执行微信配置getWxConfig(),此方法主要是获取jssdk所需要的参数,先检查本地缓存的是否过期,过期则请求后台获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | export function getWxConfig() { //判断signature是否过期 if (expireSign()) { let data = {}; data.appId = localStorage.getItem('appId'); data.timestamp = localStorage.getItem('timestamp'); data.nonceStr = localStorage.getItem('nonceStr'); data.signature = localStorage.getItem('signature'); setWxConfig(data); } else { //如果过期了,请求后台获取 let url = location.href.split('#')[0]; //获取锚点之前的链接,防止出现invalid signature错误 wxSign({ url: url }) .then(res => { console.log(res); if (res.code == 200) { localStorage.setItem('appId', res.data.appId); localStorage.setItem('timestamp', res.data.timestamp); localStorage.setItem('nonceStr', res.data.nonceStr); localStorage.setItem('signature', res.data.signature); localStorage.setItem('expireSignTime', res.data.expireTime); setWxConfig(res.data); } else { localStorage.removeItem('expireSignTime'); Toast.fail('网络故障,请退出重新加载页面'); } }) .catch(error => { localStorage.removeItem('expireSignTime'); Toast.fail('网络故障,请退出重新加载页面'); }); } } |
2:注入config配置
上面获取到后台的参数后,在页面使用wx.config接口注入权限验证配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | function setWxConfig(data) { console.log(data); wx.config({ debug: false, // true是开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: data.appId, // 必填,公众号的唯一标识 timestamp: data.timestamp, // 必填,签名的时间戳,后台生成的 nonceStr: data.nonceStr, // 必填,签名的随机串,后台生成的 signature: data.signature, // 必填,签名,后台生成的 jsApiList: ['scanQRCode'] // 必填,需要使用的JS接口列表,scanQRCode是调用扫一扫二维码 }); wx.error(function(res) { localStorage.removeItem('expireSignTime'); Toast.fail('网络故障,请退出重新页面'); }); } |
3. 方法调用
config如果不报错,则在 wx.ready里面调用jssdk的方法
1 2 3 4 5 6 7 8 9 10 11 | wx.ready(function () { wx.scanQRCode({ needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果 success: function (res) { let data = res.resultStr; // 当needResult 为 1 时,扫码返回的结果 let code = codeFormat(data); that.pointCode = code; that.saveSinglePoint(); }, }); }); |
四、后端方法
1. 获取access_token和jsapi_Ticket,缓存使用了文件的方式,也支持redis等方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /** * 获取access_token和jsapi_ticket **/ public AppWechatEntity getJsapiTicket() { //logger.debug("--------------开始执行getJsapiTicket方法--------------"); //定义过期时间 AppWechatEntity appWechatEntity = new AppWechatEntity(); String accessTokenString = ""; String jsapiTicketString = ""; String jsapi_ticket = ""; String access_token = ""; jsapiTicketString = readWechatTokenFile(getJsapiTicketFilePath()); if (!StringUtils.isEmpty(jsapiTicketString)) { appWechatEntity = JSONObject.parseObject(jsapiTicketString, AppWechatEntity.class); long expireTime = appWechatEntity.getExpire_time(); long curTime = create_timestamp(); if (expireTime >= curTime && StrUtil.isNotEmpty(appWechatEntity.getJsapi_ticket())) { //logger.debug("已有的jsapi_ticket=" + jsapi_ticket); return appWechatEntity; } } long timestamp = create_timestamp() + 7000;//过期时间是2小时(7200s) access_token = getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret); Map accessTokenMap = new HashMap(); accessTokenMap.put("expire_time", timestamp); accessTokenMap.put("access_token", access_token); accessTokenString = JSONObject.toJSONString(accessTokenMap); jsapi_ticket = getJsapiTicketData("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi"); Map jsapiTicketMap = new HashMap(); jsapiTicketMap.put("expire_time", timestamp); jsapiTicketMap.put("jsapi_ticket", jsapi_ticket); jsapiTicketString = JSONObject.toJSONString(jsapiTicketMap); // 写文件 try { FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8); FileUtils.writeStringToFile(new File(getJsapiTicketFilePath()), jsapiTicketString, CharsetUtil.CHARSET_UTF_8); //logger.debug("写入文件成功"); } catch (IOException e) { log.debug("写文件异常:" + e.getMessage()); e.printStackTrace(); } appWechatEntity.setJsapi_ticket(jsapi_ticket); appWechatEntity.setExpire_time(timestamp); appWechatEntity.setAccess_token(access_token); //logger.debug("--------------结束执行getJsapiTicket方法--------------"); return appWechatEntity; } |
2.根据jsapi_ticket获取signature
上面获取了jsapi_ticket之后,使用jsapi_ticket,noncestr,timestamp和前端传入的url组装成签名字符串,使用sha1进行加密,返回到前端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * 生成signature **/ @Override public AppWechatEntity sign(String url) { Map< String , String> ret = new HashMap(); String nonceStr = create_nonce_str(); String timestamp = Long.toString(create_timestamp()); String string1; String signature = ""; //获取jsapi_ticket和过期时间 AppWechatEntity appWechatEntity = getJsapiTicket(); String jsapiTicket = appWechatEntity.getJsapi_ticket(); Long expireTime = appWechatEntity.getExpire_time(); //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes(StandardCharsets.UTF_8)); signature = byteToHex(crypt.digest()); } catch (Exception e) { e.printStackTrace(); } appWechatEntity.setAppId(configBeanValue.appid); appWechatEntity.setExpire_time(expireTime); appWechatEntity.setNonceStr(nonceStr); appWechatEntity.setTimestamp(timestamp); appWechatEntity.setSignature(signature); return appWechatEntity; } |
3. 后端全部的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | /** * 生成signature **/ @Override public AppWechatEntity sign(String url) { Map< String , String> ret = new HashMap(); String nonceStr = create_nonce_str(); String timestamp = Long.toString(create_timestamp()); String string1; String signature = ""; //获取jsapi_ticket和过期时间 AppWechatEntity appWechatEntity = getJsapiTicket(); String jsapiTicket = appWechatEntity.getJsapi_ticket(); Long expireTime = appWechatEntity.getExpire_time(); //注意这里参数名必须全部小写,且必须有序 string1 = "jsapi_ticket=" + jsapiTicket + "&noncestr=" + nonceStr + "×tamp=" + timestamp + "&url=" + url; try { MessageDigest crypt = MessageDigest.getInstance("SHA-1"); crypt.reset(); crypt.update(string1.getBytes(StandardCharsets.UTF_8)); signature = byteToHex(crypt.digest()); } catch (Exception e) { e.printStackTrace(); } appWechatEntity.setAppId(configBeanValue.appid); appWechatEntity.setExpire_time(expireTime); appWechatEntity.setNonceStr(nonceStr); appWechatEntity.setTimestamp(timestamp); appWechatEntity.setSignature(signature); return appWechatEntity; } /** * 获取jsapi_ticket **/ public AppWechatEntity getJsapiTicket() { //logger.debug("--------------开始执行getJsapiTicket方法--------------"); //定义过期时间 AppWechatEntity appWechatEntity = new AppWechatEntity(); String accessTokenString = ""; String jsapiTicketString = ""; String jsapi_ticket = ""; String access_token = ""; jsapiTicketString = readWechatTokenFile(getJsapiTicketFilePath()); if (!StringUtils.isEmpty(jsapiTicketString)) { appWechatEntity = JSONObject.parseObject(jsapiTicketString, AppWechatEntity.class); long expireTime = appWechatEntity.getExpire_time(); long curTime = create_timestamp(); if (expireTime >= curTime && StrUtil.isNotEmpty(appWechatEntity.getJsapi_ticket())) { //logger.debug("已有的jsapi_ticket=" + jsapi_ticket); return appWechatEntity; } } long timestamp = create_timestamp() + 7000;//过期时间是2小时(7200s) access_token = getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret); Map accessTokenMap = new HashMap(); accessTokenMap.put("expire_time", timestamp); accessTokenMap.put("access_token", access_token); accessTokenString = JSONObject.toJSONString(accessTokenMap); jsapi_ticket = getJsapiTicketData("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + access_token + "&type=jsapi"); Map jsapiTicketMap = new HashMap(); jsapiTicketMap.put("expire_time", timestamp); jsapiTicketMap.put("jsapi_ticket", jsapi_ticket); jsapiTicketString = JSONObject.toJSONString(jsapiTicketMap); // 写文件 try { FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8); FileUtils.writeStringToFile(new File(getJsapiTicketFilePath()), jsapiTicketString, CharsetUtil.CHARSET_UTF_8); //logger.debug("写入文件成功"); } catch (IOException e) { log.debug("写文件异常:" + e.getMessage()); e.printStackTrace(); } appWechatEntity.setJsapi_ticket(jsapi_ticket); appWechatEntity.setExpire_time(timestamp); appWechatEntity.setAccess_token(access_token); //logger.debug("--------------结束执行getJsapiTicket方法--------------"); return appWechatEntity; } public String getAccessToken() { //logger.debug("--------------开始执行getAccessToken方法--------------"); String access_token = ""; String accessTokenString = ""; AppWechatEntity appWechatEntity = new AppWechatEntity(); accessTokenString = readWechatTokenFile(getAccessTokenFilePath()); if (StringUtils.isNotEmpty(accessTokenString)) { appWechatEntity = JSONObject.parseObject(accessTokenString, AppWechatEntity.class); access_token = appWechatEntity.getAccess_token(); long expireTime = appWechatEntity.getExpire_time(); long curTime = create_timestamp(); if (expireTime >= curTime) { //logger.debug("已有的access_token=" + access_token); return access_token; } } long timestamp = create_timestamp() + 6000;//过期时间是2小时,但是可以提前进行更新,防止前端正好过期 access_token = getAccessTokenData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + configBeanValue.appid + "&secret=" + configBeanValue.secret); Map accessTokenMap = new HashMap(); accessTokenMap.put("expire_time", timestamp); accessTokenMap.put("access_token", access_token); accessTokenString = JSONObject.toJSONString(accessTokenMap); // 写文件 try { FileUtils.writeStringToFile(new File(getAccessTokenFilePath()), accessTokenString, CharsetUtil.CHARSET_UTF_8); //logger.debug("写入文件成功"); } catch (IOException e) { log.debug("写文件异常:" + e.getMessage()); e.printStackTrace(); } //logger.debug("新的access_token=" + access_token); //logger.debug("--------------结束执行getAccessToken方法--------------"); return access_token; } private String readWechatTokenFile(String filePath) { String content = ""; try { if (new File(filePath).exists()) { FileReader fileReader = new FileReader(filePath, CharsetUtil.CHARSET_UTF_8); content = fileReader.readString(); } else { new File(filePath).createNewFile(); } } catch (IOException e) { log.error("读文件异常:" + e.getMessage()); e.printStackTrace(); } return content; } private String getAccessTokenData(String url) { String str = ""; String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8); if (StringUtils.isBlank(result)) return str; str = parseData("access_token", "expires_in", result); return str; } private String getJsapiTicketData(String url) { String str = ""; String result = HttpUtil.get(url, CharsetUtil.CHARSET_UTF_8); if (StringUtils.isBlank(result)) return str; str = parseData("ticket", "expires_in", result); return str; } private String parseData(String tokenName, String expiresInName, String data) { String tokenConent = ""; JSONObject jsonObject = JSONObject.parseObject(data); try { tokenConent = jsonObject.get(tokenName).toString(); if (StringUtils.isEmpty(tokenConent)) { log.error("token获取失败,获取结果" + data); return tokenConent; } } catch (Exception e) { log.error("token 结果解析失败,token参数名称: " + tokenName + "有效期参数名称:" + expiresInName + "token请求结果:" + data); e.printStackTrace(); return tokenConent; } return tokenConent; } private String byteToHex(final byte[] hash) { Formatter formatter = new Formatter(); for (byte b : hash) { formatter.format("%02x", b); } String result = formatter.toString(); formatter.close(); return result; } private String create_nonce_str() { return IdUtil.simpleUUID(); } private Long create_timestamp() { return System.currentTimeMillis() / 1000; } private String getJsapiTicketFilePath() { return configBeanValue.tokenpath + "/" + configBeanValue.appid + "_jsapiTicket.txt"; } private String getAccessTokenFilePath() { return configBeanValue.tokenpath + "/" + configBeanValue.appid + "_accessToken.txt"; } |
五.vue weixin-js-sdk进行微信分享
第一步:安装weixin-js-sdk
1 | npm install weixin-js-sdk |
第二步:
在assets文件下新建个common文件夹 ,然后再新建个utils.js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | import wx from "weixin-js-sdk"; /* * 微信分享 * 获取微信加签信息 * @param{data}:获取的微信加签 * @param{shareData}:分享配置参数 */ export const wexinShare = (data, shareData) => { let appId = data.appId; let timestamp = data.timestamp; let nonceStr = data.nonceStr; let signature = data.signature; wx.config({ debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId: appId, // 必填,公众号的唯一标识 timestamp: timestamp, // 必填,生成签名的时间戳 nonceStr: nonceStr, // 必填,生成签名的随机串 signature: signature, // 必填,签名,见附录1 jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function () { //分享到朋友圈”及“分享到QQ空间” wx.updateTimelineShareData({ title: shareData.title, // 分享标题 link: shareData.link, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致 imgUrl: shareData.imgUrl, // 分享图标 success: function (res) { // 设置成功 // console.log("分享成功返回的信息为:", res); } }) //“分享给朋友”及“分享到QQ” wx.updateAppMessageShareData({ title: shareData.title, // 分享标题 desc: shareData.desc, // 分享描述 link: shareData.link, // 分享链接 imgUrl: shareData.imgUrl, // 分享图标 success: function (res) { // console.log("分享成功返回的信息为:", res);; } }) }); wx.error(function (res) { // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 console.log('验证失败返回的信息:', res); }); } |
第三步:调用
1 | import {wexinShare} from "@/common/utils.js"; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | //请求微信配置参数接口(获取签名),由后台给接口给 get_share(){ var that = this; var urls = that.$qs.stringify(window.location.href.split('#')[0]); //看后台请求接口是get/post that.$axios.post("/xxx",urls).then((res) => { if (res.data) { //微信加签 var obj = { appId: res.data.appId, nonceStr: res.data.nonceStr, signature: res.data.signature, timestamp: res.data.timestamp } //分享数据,这段主要是为了在hash模式下分享出去的链接不被浏览器截取,保证完全把链接分享出去 var shareWxLink = window.location.href.split('#')[0] + 'static/redirect.html?redirect=' + encodeURIComponent(window.location.href) + '&pid_uid=' + userinfo.uid; // console.log('shareWxLink', shareWxLink) let shareData = { title: '标题', // 分享标题 link: shareWxLink, imgUrl: 'http://xxx.jpg', // 分享图标 desc: '这里填写简介文字' } //引用 wexinShare(obj, that, shareData); } else { that.$toast('获取sdk参数失败!'); } }) } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· 【全网最全教程】使用最强DeepSeekR1+联网的火山引擎,没有生成长度限制,DeepSeek本体
2021-01-06 uni-app 获取地址位置