解析微信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 相关文章推荐
juqery 学习之三 选择器 可见性 元素属性
Nov 25 Javascript
最简单的js图片切换效果实现代码
Sep 24 Javascript
基于jquery的防止大图片撑破页面的实现代码(立即缩放)
Oct 24 Javascript
THREE.JS入门教程(5)你应当知道的十件事
Jan 24 Javascript
解决IE6的PNG透明JS插件使用介绍
Apr 17 Javascript
jQuery 写的简单打字游戏可以提示正确和错误的次数
Jul 01 Javascript
Underscore.js常用方法总结
Feb 28 Javascript
js漂浮广告实现代码
Aug 15 Javascript
有关jQuery中parent()和siblings()的小问题
Jun 01 Javascript
深入理解requestAnimationFrame的动画循环
Sep 20 Javascript
vue-cli项目代理proxyTable配置exclude的方法
Sep 20 Javascript
javascript中如何判断类型汇总
May 14 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实现的MySQL通用查询程序
2007/03/11 PHP
php 批量生成html,txt文件的实现代码
2013/06/26 PHP
PHP基于面向对象实现的留言本功能实例
2018/04/04 PHP
用JS判别浏览器种类以及IE版本的几种方法小结
2011/08/02 Javascript
JavaScript中OnLoad几种使用方法
2012/12/15 Javascript
几种延迟加载JS代码的方法加快网页的访问速度
2013/10/12 Javascript
jquery监听div内容的变化具体实现思路
2013/11/04 Javascript
jQuery拖动div、移动div、弹出层实现原理及示例
2014/04/08 Javascript
基于Bootstrap使用jQuery实现输入框组input-group的添加与删除
2016/05/03 Javascript
JS添加删除DIV的简单实例
2016/07/08 Javascript
js插件Jcrop自定义截取图片功能
2016/10/14 Javascript
如何解决vue与传统jquery插件冲突
2017/03/20 Javascript
AngularJS 支付倒计时功能实现思路
2017/06/05 Javascript
使用原生js封装的ajax实例(兼容jsonp)
2017/10/12 Javascript
老生常谈JavaScript获取CSS样式的方法(兼容各浏览器)
2018/09/19 Javascript
jQuery中event.target和this的区别详解
2020/08/13 jQuery
详细分析JavaScript中的深浅拷贝
2020/09/17 Javascript
Python fileinput模块使用介绍
2014/11/30 Python
Python数组遍历的简单实现方法小结
2016/04/27 Python
Python 备份程序代码实现
2017/03/06 Python
keras小技巧——获取某一个网络层的输出方式
2020/05/23 Python
Python中SQLite如何使用
2020/05/27 Python
python爬虫beautifulsoup解析html方法
2020/12/07 Python
Python读写Excel表格的方法
2021/03/02 Python
SCHIESSER荷兰官方网站:德国内衣专家
2020/10/09 全球购物
电子商务专业实习生自我鉴定
2013/09/24 职场文书
仓管员岗位职责范文
2013/11/08 职场文书
高考备战决心书
2014/03/11 职场文书
晚会主持词开场白
2014/03/17 职场文书
雷锋精神演讲稿
2014/05/13 职场文书
乡镇干部先进性教育活动个人整改措施
2014/09/16 职场文书
2014年初中班主任工作总结
2014/11/08 职场文书
离退休人员聘用协议书
2014/11/24 职场文书
党员个人承诺书
2015/04/27 职场文书
2016年公司“3.12”植树节活动总结
2016/03/16 职场文书
golang DNS服务器的简单实现操作
2021/04/30 Golang