解析微信JS-SDK配置授权,实现分享接口


Posted in Javascript onDecember 09, 2016

微信开放的JS-SDK面向网页开发者提供了基于微信内的网页开发工具包,最直接的好处就是我们可以使用微信分享、扫一扫、卡券、支付等微信特有的能力。7月份的时候,因为这个分享的证书获取问题深深的栽了一坑,后面看到“config:ok”的时候真的算是石头落地,瞬间感觉世界很美好..

这篇文章是微信开发的很多前置条件,包括了服务端基于JAVA的获取和缓存全局的access_token,获取和缓存全局的jsapi_ticket,以及前端配置授权组件封装,调用分享组件封装。

配置授权思路:首先根据access_token获取jsapi_ticket,在通过获取到的jsapi_ticket以及随机生成的字符串、时间戳,再加上需要授权的页面地址url,进行SHA-1加密,返回加密字符串,最后根据加密串调用微信提供的config接口。

配置JS接口安全域名

公众平台--公众号设置--功能设置--js接口安全域名

解析微信JS-SDK配置授权,实现分享接口

获取、缓存全局的access_token

/**
  * 微信全局票据 ---->>>> access_token
  * @return
  * @throws ClientProtocolException
  * @throws IOException
  */
 public String getBaseAccessToken() throws ClientProtocolException, IOException{ 
  try {
   String value = redisService.get("WEIXIN_BASE_ACCESS_TOKEN");
   if (!StringUtils.isEmpty(value)) {
    LOGGER.info("Get base access_token from redis is successful.value:{}",value);
    return value;
   }else{
    synchronized (this) {
     //缓存中没有、或已经失效
     String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+WX_APPID+"&secret="+ WX_APPSECRET;
     String rs = apiService.doGet(url);
     
     JSONObject obj_content = JSONObject.parseObject(rs);
     String accessToken = obj_content.getString("access_token");
     Integer time = Integer.parseInt(obj_content.getString("expires_in").toString());
     
     //写缓存
     redisService.set("WEIXIN_BASE_ACCESS_TOKEN", accessToken, time - 3600);
     LOGGER.info("Set base access_token to redis is successful.parameters time:{},realtime",time,time-3600);
     return accessToken;
    }
   }
  } catch (Exception e) {
   LOGGER.error("Get base access_token from redis is error.");
  }
  return null;
 }

先从缓存中取key为“WX_BASE_ACCESS_TOKEN” ,如果命中直接返回值,反之通过httpclient发送GET请求调用微信提供的接口获取全局的access_token,同时将取到的值写入缓存。

获取、缓存全局的jsapi_ticket

/**
  * jsapi_ticket是公众号用于调用微信JS接口的临时票据
  * @return
  * @throws IOException 
  * @throws ClientProtocolException 
  */
 public String getJsapiTicket() throws ClientProtocolException, IOException{
  try {
   String value = redisService.get("WEIXIN_JS_API_TICKET");
   if (!StringUtils.isEmpty(value)) {
    return value;
   }else{
    synchronized (this) {
     //缓存中没有、或已经失效
     //获取全局的access_token,唯一票据
     String accessToken = getBaseAccessToken();
     String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+ accessToken +"&type=jsapi";
     String rs = apiService.doGet(url);
     JSONObject obj_content = JSONObject.parseObject(rs);
     String jsapi_ticket = obj_content.getString("ticket");
     Integer time = Integer.parseInt(obj_content.getString("expires_in").toString());
     //写缓存
     redisService.set("WEIXIN_JS_API_TICKET", jsapi_ticket, time - 3600);
     return jsapi_ticket;
    }
   }
  } catch (Exception e) {
   LOGGER.error("Get js_api_ticket from redis is error:{}",e);
  }
  return null;
 }

由于获取jsapi_ticket微信有100000次限制,所以必须用上缓存。同理获取access_token,我这里为了保险起见缓存失效时间设置为官方提供的时间再减去一个小时。

jssdk加密串获取restful

1.Controller

/**
  * 微信分享证书获取
  * @param 
  * @return signature
  * @throws IOException 
  */
 @RequestMapping(value = "/signature", method = RequestMethod.GET)
 public @ResponseBody String createSignature(
        @RequestParam String url) throws IOException{
  LOGGER.info("RestFul of createSignature parameters url:{}",url);
  try {
   String rs = wechatService.createSignature(url);
   LOGGER.info("RestFul of signature is successful.",rs);
   return rs;
  } catch (Exception e) {
   LOGGER.error("RestFul of signature is error.",e);
  }
  return null;
 }

2.Service

/**
  * 根据jsapi_ticket等参数进行SHA1加密
  * @param nonceStr 随机字符串
  * @param timestamp 当前时间戳
  * @param url 当前页面url
  */
 public String createSignature(String url) throws ClientProtocolException, IOException{
  String nonceStr = create_nonce_str();
  String timestamp = create_timestamp();
  
  String signature = "jsapi_ticket="+getJsapiTicket();
    signature += "&noncestr="+nonceStr;
    signature += "×tamp="+timestamp;
    signature += "&url="+url;
  try {
   MessageDigest crypt = MessageDigest.getInstance("SHA-1");
   crypt.reset();
   crypt.update(signature.getBytes("UTF-8"));
   signature = byteToHex(crypt.digest());
  } catch (Exception e) {
   LOGGER.error("Signature for SHA-1 is error:{}",e);
  }
  Map<String, String> map = new HashMap<String, String>();
  map.put("timestamp", timestamp);
  map.put("nonceStr", nonceStr);
  map.put("signature", signature);
  map.put("appid", WX_APPID);
  return JSON.toJSONString(map, true);
 }
 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;
 }

WX_APPID为公众号appid,通过spring@value注解从配置文件获取,这里不细说。

3.生成随机字符串

private static String create_nonce_str() {
  return UUID.randomUUID().toString();
 }

4.时间格式化

private static String create_timestamp() {
  return Long.toString(System.currentTimeMillis() / 1000);
 }

到此为止后台全部完成,其实没有太多的解释,仔细读一遍代码,可读性应该还行!

封装获取授权组件,实现分享方法

require.config({
 urlArgs: "v=20161116" ,
 baseUrl : "/static",
 paths: {
  jweixin: 'component/jweixin/jweixin-1.0.0',
  share: 'component/wechat/share'//微信分享组件
 }
})

首先通过调用后台接口获取加密字符串,调用微信提供的wx.config()方法

//jsSDK授权
 $.signature = function(wx,opts,currentUrl,callback){
  $.ajax({
   data: {url: currentUrl},
   type: "GET",
   url: WX_ROOT + "wechat/signature",
   success: function (json) {
    if (json) {
     var data = JSON.parse(json); 
     wx.config({
      debug: false,
      appId: data.appid,
      timestamp: data.timestamp,
      nonceStr: data.nonceStr,
      signature: data.signature,
      jsApiList: [
       'onMenuShareTimeline',
       'onMenuShareAppMessage',
       'onMenuShareQQ',
       'onMenuShareWeibo',
       'onMenuShareQZone'
      ]
     });
     wechatShare.options.isSignature = true;
     callback && callback(opts,wx);
    }
   }
  });
 }

建议:开发环境建议开启调式模式,方便打印日志定位问题debug: true  

所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,我这里用一个全局变量isSignature缓存了是否已经配置授权,然后执行回调。如实现分享接口:

//分享
 $.share = function(opts,wx) {
  var options = {
    currentUrl: window.location.href.split('#')[0],
    imgUrl: null,
    title: '达农保险',
    desc: null,
    shareUrl: null
   }
  $.extend(true, options, opts || {});
  //判断是否已经授权
  if(!wechatShare.options.isSignature){
   $.signature(wx,opts,options.currentUrl,$.share)
  }else{
   wx.ready(function(){
    //分享到朋友圈
    wx.onMenuShareTimeline({
     title: options.title,
     link: options.shareUrl,
     imgUrl: options.imgUrl,
     success: function () {
      //分享统计,分享来源 1 朋友圈 2分享给朋友 3分享到QQ 4分享到QQ空间 
     }
    });
    //分享给朋友
    wx.onMenuShareAppMessage({
     title: options.title,
     desc: options.desc,
     link: options.shareUrl,
     imgUrl: options.imgUrl
    });
   });
  } 
 }

我先确认是否已经配置授权,如果没有授权则调用$.signature()回调函数里传入$.share,有点类似递归调用,当再次回到share方法的时候isSignature已经是true了,则执行wx.ready()方法,再调需要调用的接口,微信开放提供了很多接口给我们,分享只是其中一个。只有想不到的,没有实现不了的....

注意:currentUrl 必须是动态获取的,通过window.location.href方法,因为页面分享,微信客户端会在你的链接末尾加入其它参数,所以需要再将url用‘#'割一下,取第一个,如果有中文最好是用encodeURIComponent转义一下,保证签名获取成功。如果报invalid signature,大部分原因是传入的url,和加密算法的问题,仔细检查!

调用:

var ua = navigator.userAgent.toLowerCase(),
 isWechat = ua.indexOf('micromessenger') != -1;//判断是否为微信浏览器
var shareData = {

title: ‘测试分享',


desc: ‘这里是描述,分享到朋友圈不会显示',


link: APP_ROOT + '/base/downloadApp,//分享后打开的链接,必须在配置的安全域名下


imgUrl: PIC_PATH + (self.data.shareListData[0].imgSmall || '/static/img/coupon/getTicPic.png'),//分享后显示的图片


success: function(){
      setTimeout(function(){







//运营数据统计






 },0)//伪异步方式调用
     }




}
//微信浏览器分享加载
if(isWechat){
 require(['jweixin'],function(wx){


require(['share'],function(){
   $.share(shareData,wx);


})

})
}

完整js:https://github.com/helijun/component/blob/master/wechat/share.js

常用问题总结:

最开始做这个分享功能的时候,因为一个证书获取失败的原因(invalid signature)真的是断断续续困了好几天,有的时候真的是毫无头绪了。反复检查代码,逐字逐行的看,真的没有发现任何异常,通过微信提供的一个js接口签名校验工具测试也是返回ture,然而就是报证书失败!微信官方文档又有点模棱两可,到最后星期六的一个下午,静下心来,再耐心的检查了一遍后台SHA1加密算法,终于看到config true..  曙光

开发中我们总是会遇到各种各样的问题,程序员和bug永远都是好朋友同时又是敌人,我们总是徘徊在bug的边缘,有时候当遇到很奇怪的问题的时候不妨先放一下,注意力先转移一下,去阳台吹吹风,说不定在某一个时刻,问题突然就解开了..

接口签名校验工具

https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持三水点靠木!

Javascript 相关文章推荐
jquery autocomplete自动完成插件的的使用方法
Aug 07 Javascript
js预加载图片方法汇总
Jun 15 Javascript
Javascript类型系统之String字符串类型详解
Jun 21 Javascript
JavaScript中三个等号和两个等号的区别(== 和 ===)浅析
Sep 22 Javascript
angular学习之从零搭建一个angular4.0项目
Jul 10 Javascript
PHP自动加载autoload和命名空间的应用小结
Dec 01 Javascript
vue实现商城秒杀倒计时功能
Dec 12 Javascript
Javascript Worker子线程代码实例
Feb 20 Javascript
基于JavaScript实现留言板功能
Mar 16 Javascript
Laravel 如何在blade文件中使用Vue组件的示例代码
Jun 28 Javascript
解决Vue的文本编辑器 vue-quill-editor 小图标样式排布错乱问题
Aug 03 Javascript
浅析我对JS延迟异步脚本的思考
Oct 12 Javascript
利用D3.js实现最简单的柱状图示例代码
Dec 09 #Javascript
适用于手机端的jQuery图片滑块动画
Dec 09 #Javascript
Angular.Js的自动化测试详解
Dec 09 #Javascript
浅析javascript中的Event事件
Dec 09 #Javascript
清除js缓存的多种方法总结
Dec 09 #Javascript
Vue.js计算属性computed与watch(5)
Dec 09 #Javascript
JS新包管理工具yarn和npm的对比与使用入门
Dec 09 #Javascript
You might like
PHP 身份验证方面的函数
2009/10/11 PHP
php站内搜索并高亮显示关键字的实现代码
2011/12/29 PHP
用穿越火线快速入门php面向对象
2012/02/22 PHP
中高级PHP程序员应该掌握哪些技术?
2016/09/23 PHP
php 删除一维数组中某一个值元素的操作方法
2018/02/01 PHP
Laravel服务容器绑定的几种方法总结
2020/06/14 PHP
游戏人文件夹程序 ver 3.0
2006/07/14 Javascript
dojo 之基础篇
2007/03/24 Javascript
jquery 面包屑导航 具体实现
2013/06/05 Javascript
js字母大小写转换实现方法总结
2013/11/13 Javascript
JS中判断字符串中出现次数最多的字符及出现的次数的简单实例
2016/06/03 Javascript
javascript对浅拷贝和深拷贝的详解
2016/10/14 Javascript
详解jquery validate实现表单验证 (正则表达式)
2017/01/18 Javascript
jQuery插件FusionCharts绘制2D柱状图和折线图的组合图效果示例【附demo源码】
2017/04/10 jQuery
基于JavaScript实现类名的添加与移除
2017/04/23 Javascript
JS解析后台返回的JSON格式数据实例
2018/08/06 Javascript
jQuery UI实现动画效果代码分享
2018/08/19 jQuery
vue自定v-model实现表单数据双向绑定问题
2018/09/03 Javascript
Nodejs处理异常操作示例
2018/12/25 NodeJs
[14:03]2017DOTA2亚洲邀请赛开幕式:12神兵演绎水墨中华
2017/04/01 DOTA
python遍历数组的方法小结
2015/04/30 Python
Python中矩阵创建和矩阵运算方法
2018/08/04 Python
python-pyinstaller、打包后获取路径的实例
2019/06/10 Python
python实现操作文件(文件夹)
2019/10/31 Python
Python实现线性判别分析(LDA)的MATLAB方式
2019/12/09 Python
python中matplotlib实现随鼠标滑动自动标注代码
2020/04/23 Python
input元素的url类型和email类型简介
2012/07/11 HTML / CSS
如果有两个类A,B,怎么样才能使A在发生一个事件的时候通知B
2016/03/12 面试题
写好求职应聘自荐信的三部曲
2013/09/21 职场文书
计算机专业毕业生自荐书
2014/06/02 职场文书
电子商务专业毕业生求职信
2014/06/12 职场文书
打架赔偿协议书范本
2014/10/26 职场文书
2015年教师节贺卡寄语
2015/03/24 职场文书
2015年生产车间工作总结
2015/04/22 职场文书
通知的写法
2015/04/23 职场文书
2015年林业工作总结
2015/05/14 职场文书