記(ji)錄--微信調用jssdk全流程詳解
這里給大家分享我(wo)在網上(shang)總結出來的一(yi)些(xie)知識,希(xi)望(wang)對大家有所(suo)幫助
微信調用jssdk全流程詳解
系統框架使用的是前后端分離(li),前端使用vant,后端是springboot
一、網頁授權的時序圖

二、公眾號配置
1. 綁定域名
登錄微(wei)信公眾(zhong)平(ping)臺(tai)進入“公眾(zhong)號設置(zhi)”的(de)“功(gong)能設置(zhi)”里填寫“JS接口安(an)全域名”。也就(jiu)是這樣:

點擊設置之后,彈出這樣(yang)一個(ge)輸入框,輸入服務器(qi)所在的域名:

2:引入js文件
直接(jie)在你的頁面里引入js文(wen)件就行
<script src="//res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
三、前端方法
1. 初始化方法,從后臺獲取基本的參數
前端進入需(xu)要掃一掃功能的頁面時候,在mounted方(fang)法(fa)里面,執行微信配置getWxConfig(),此方(fang)法(fa)主要是獲取(qu)jssdk所需(xu)要的參(can)數,先檢查本(ben)地緩(huan)存(cun)的是否過期,過期則請求后臺(tai)獲取(qu)
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配置
上面獲取到(dao)后(hou)臺的參數后(hou),在頁面使用wx.config接口注入(ru)權限(xian)驗(yan)證配置
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("//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("//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
上面獲取(qu)了jsapi_ticket之后(hou),使用(yong)(yong)jsapi_ticket,noncestr,timestamp和前端(duan)傳入的(de)url組裝成簽名字符串,使用(yong)(yong)sha1進行加密,返回(hui)到前端(duan)
/**
* 生成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("//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("//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("//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文件(jian)下新(xin)建個(ge)common文件(jian)夾(jia) ,然后再新(xin)建個(ge)utils.js文件(jian)

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);
});
}
第(di)三步:調用
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: '//xxx.jpg', // 分享圖標
desc: '這里填寫簡介文字'
}
//引用
wexinShare(obj, that, shareData);
} else {
that.$toast('獲取sdk參數失敗!');
}
})
}


