系统框架使用的是前后端分离,前端使用vant,后端是springboot
登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。也就是这样:
直接在你的页面里引入js文件就行
前端进入需要扫一扫功能的页面时候,在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('网络故障,请退出重新加载页面');
});
}
}
上面获取到后台的参数后,在页面使用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('网络故障,请退出重新页面');
});
}
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;
}
上面获取了jsapi_ticket之后,使用jsapi_ticket,noncestr,timestamp和前端传入的url组装成签名字符串,使用sha1进行加密,返回到前端
/**
* 生成signature
**/
@Override
public AppWechatEntity sign(String url) {
Map 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;
}
/**
* 生成signature
**/
@Override
public AppWechatEntity sign(String url) {
Map 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";
}