這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 微信調用jssdk全流程詳解 系統框架使用的是前後端分離,前端使用vant,後端是springboot 一、網頁授權的時序圖 二、公眾號配置 1. 綁定功能變數名稱 登錄微信公眾平臺進入“公眾號設置”的“功能設置”里填寫“JS介面安全功能變數名稱”。也就 ...
這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助
微信調用jssdk全流程詳解
系統框架使用的是前後端分離,前端使用vant,後端是springboot
一、網頁授權的時序圖
二、公眾號配置
1. 綁定功能變數名稱
登錄微信公眾平臺進入“公眾號設置”的“功能設置”里填寫“JS介面安全功能變數名稱”。也就是這樣:
點擊設置之後,彈出這樣一個輸入框,輸入伺服器所在的功能變數名稱:
2:引入js文件
直接在你的頁面里引入js文件就行
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
三、前端方法
1. 初始化方法,從後臺獲取基本的參數
前端進入需要掃一掃功能的頁面時候,在mounted方法裡面,執行微信配置getWxConfig(),此方法主要是獲取jssdk所需要的參數,先檢查本地緩存的是否過期,過期則請求後臺獲取
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介面註入許可權驗證配置
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的方法
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等方式
/** * 獲取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進行加密,返回到前端
/** * 生成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. 後端全部的代碼
/** * 生成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
npm install weixin-js-sdk
第二步:
在assets文件下新建個common文件夾 ,然後再新建個utils.js文件
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); }); }
第三步:調用
import {wexinShare} from "@/common/utils.js";
//請求微信配置參數介面(獲取簽名),由後臺給介面給 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參數失敗!'); } }) }