最近碰到了这么一个需求,说是在前端页面调用手机本地的相机,扫描二维码这么一个需求,对于我一个后端来说,
这实在是难,难于上青天,但是决不能说一个不字.我说可以使用微信的扫码工具吗,这样可以方便一点,...(起码有个思路)
看着微信文档,一步一步坑下去.不对,是走下去.
这里我们用测试是公众号,方便测试.
首先配置自己的appID和appsecret,这里的测试帐号直接就给出了
第二步: 验证服务器,这个很简单,按照文档的规则验证就好了
第三步: 直接使用二级域名,不能添加http://前缀,或者是/XXX你自己写的接口(亲测)
这里还有个问题就是下载一个XXXXX.txt文件放在项目的根目录下,这个txt文件在公众号里面设置js安全域名下面可以下载
第四步: 生成微信签名,这个很坑,要小心生成,这里面我使用的是google的guava做缓存,结合网上大佬的一个案例,仅限参考
public static Map<String, Object> sign(String url) throws Exception {
String ticket = getTicketToken(WeChatConstant.TICKET_TOKEN);
Map<String, Object> ret = new HashMap<String, Object>();
String nonce_str = create_nonce_str();
String timestamp = create_timestamp();
String string1;
String signature = "";
//注意这里参数名必须全部小写,且必须有序
string1 = "jsapi_ticket=" + ticket +"&noncestr=" + nonce_str +"×tamp=" + timestamp +"&url=" + url;
System.out.println(string1);
try{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
System.out.println(signature+"=====================");
}catch (NoSuchAlgorithmException e){
e.printStackTrace();
}catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
ret.put("url", url);
ret.put("nonceStr", nonce_str);
ret.put("timestamp", timestamp);
ret.put("signature", signature);
ret.put("jsapi_ticket", ticket);
ret.put("appId", WeChatConstant.APPID);
return ret;
}
/**
* 缓存其accessToken 和 ticketToken 的值
*
* @param key
* @return
* @throws Exception
*/
public static String getTicketToken(String key) throws Exception{
if (Objects.isNull(cache) || getCacheAccessToken(key).equals(WeChatConstant.TICKET_TOKEN)){
Gson gson = new Gson();
cache = CacheBuilder.newBuilder().expireAfterWrite(7100,TimeUnit.SECONDS).build();
String accessToken = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+WeChatConstant.APPID +"&secret="+WeChatConstant.SECRET;
String httpResult = getHttpResult(accessToken);
AccessToken aT = gson.fromJson(httpResult, AccessToken.class);
String signUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+aT.getAccess_token()+"&type=jsapi";
String resultSign = getHttpResult(signUrl);
TicketToken ticket = gson.fromJson(resultSign, TicketToken.class);
cache.put(WeChatConstant.TICKET_TOKEN,ticket.getTicket());
return ticket.getTicket();
}else{
return getCacheAccessToken(key);
}
}
/**
* token失效,返回key值
*
* @param key
* @return
* @throws ExecutionException
*/
private static String getCacheAccessToken(String key) throws ExecutionException {
return cache.get(key, () -> {
return key;
});
}
/**
* 随机加密
* @param hash
* @return
*/
private static 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 static String getHttpResult(String url) {
String result = "";
HttpGet httpRequest = new HttpGet(url);
try {
HttpResponse httpResponse = HttpClients.createDefault().execute(httpRequest);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
result = EntityUtils.toString(httpResponse.getEntity());
}
} catch (ClientProtocolException e) {
e.printStackTrace();
result = e.getMessage().toString();
} catch (IOException e) {
e.printStackTrace();
result = e.getMessage().toString();
}
return result;
}
/**
* 产生随机串--由程序自己随机产生
* @return
*/
private static String create_nonce_str() {
return UUID.randomUUID().toString();
}
/**
* 由程序自己获取当前时间
* @return
*/
private static String create_timestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
第五步:返回数据给前端校验
/**
* 调用扫一扫 验证用户信息
*
* @param request
* @return
*/
@GetMapping("/signature")
public WeChatEntity signature(HttpServletRequest request) throws Exception {
Map<String, Object> resMap = new HashMap<String, Object>();
resMap = SignatureUtil.sign(request.getRequestURL().);
request.setAttribute("nonceStr", resMap.get("nonceStr"));
request.setAttribute("timestamp", resMap.get("timestamp"));
request.setAttribute("signature", resMap.get("signature"));
request.setAttribute("appId", resMap.get("appId"));
request.setAttribute("url", resMap.get("url"));
request.setAttribute("jsapi_ticket",resMap.get("jsapi_ticket"));
WeChatEntity weChatEntity = new WeChatEntity(
resMap.get("appId").toString(),
resMap.get("timestamp").toString(),
resMap.get("nonceStr").toString(),
resMap.get("signature").toString());
return weChatEntity;
}
这里有个坑的地方就是url的设定,这是你请求当前页面的url,可以在前端使用
location.href.split('#')[0]打印出来.看看是否符合.
至此后端程序告一段落,下面就是前端程序了
1.导入依赖
<script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
html代码:
<input type="button" value="扫一扫" id="scanQRCode">
js代码:
$(function() {
$.get("/signature", function(data){
alert(location.href.split('#')[0]);
// alert(data.appId
// +"======"+ data.timestamp
// +"======"+data.nonceStr
// +"======"+data.signature);
//上面是查看传来的数据是否符合要求
wx.config({
// 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
debug: false,
// 必填,公众号的唯一标识
appId: data.appId,
// 必填,生成签名的时间戳
timestamp: "" + data.timestamp,
// 必填,生成签名的随机串
nonceStr: data.nonceStr,
// 必填,签名
signature: data.signature,
// 必填,需要使用的JS接口列表
jsApiList: ['checkJsApi', 'scanQRCode']
});
});
wx.error(function(res) {
alert("出错了:" + res.errMsg); //这个地方的好处就是wx.config配置错误,会弹出窗口哪里错误,然后根据微信文档查询即可。
});
wx.ready(function() {
wx.checkJsApi({
jsApiList: ['scanQRCode'],
success: function(res) {
}
});
//点击按钮扫描二维码
document.querySelector('#scanQRCode').onclick = function() {
wx.scanQRCode({
needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
scanType: ["qrCode"], // 可以指定扫二维码还是一维码,默认二者都有
success: function(res) {
var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
alert("扫描结果:" + result);
window.location.href = result; //因为我这边是扫描后有个链接,然后跳转到该页面
}
});
};
});});
以上就是网上一些大佬还有结合 详细说明的官方文档,花费了一下午时间,踩了N个坑总结出来的,希望大家都可以完成功能,这样就可以开心的扫一扫啦,我先去扫一下了
有问题可以一起沟通,交流哈
放几个连接,大家可以参考下
微信签名工具
微信公众平台, config:invalid signature一直爆这个错误,求教如何解决?