系统框架使用的是前后端分离,前端使用vant,后端是springboot

登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。也就是这样:

点击设置之后,弹出这样一个输入框,输入服务器所在的域名:

直接在你的页面里引入js文件就行
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
前端进入需要扫一扫功能的页面时候,在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();
},
});
});
/**
* 获取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<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;
}
/**
* 生成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";
}
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参数失败!');
}
})
}
Sinatra新手;我正在运行一些rspec测试,但在日志中收到了一堆不需要的噪音。如何消除日志中过多的噪音?我仔细检查了环境是否设置为:test,这意味着记录器级别应设置为WARN而不是DEBUG。spec_helper:require"./app"require"sinatra"require"rspec"require"rack/test"require"database_cleaner"require"factory_girl"set:environment,:testFactoryGirl.definition_file_paths=%w{./factories./test/
我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file
我有两个Rails模型,即Invoice和Invoice_details。一个Invoice_details属于Invoice,一个Invoice有多个Invoice_details。我无法使用accepts_nested_attributes_forinInvoice通过Invoice模型保存Invoice_details。我收到以下错误:(0.2ms)BEGIN(0.2ms)ROLLBACKCompleted422UnprocessableEntityin25ms(ActiveRecord:4.0ms)ActiveRecord::RecordInvalid(Validationfa
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我需要一些关于TDD概念的帮助。假设我有以下代码defexecute(command)casecommandwhen"c"create_new_characterwhen"i"display_inventoryendenddefcreate_new_character#dostufftocreatenewcharacterenddefdisplay_inventory#dostufftodisplayinventoryend现在我不确定要为什么编写单元测试。如果我为execute方法编写单元测试,那不是几乎涵盖了我对create_new_character和display_invent
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时
前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立
项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU