解析微信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 相关文章推荐
利用js跨页面保存变量做菜单的方法
Jan 17 Javascript
JS小框架 fly javascript framework
Nov 26 Javascript
jquery禁用右键单击功能屏蔽F5刷新
Mar 17 Javascript
javascript中的 object 和 function小结
Aug 14 Javascript
手机Web APP如何实现分享多平台功能
Aug 19 Javascript
jQuery电话号码验证实例
Jan 05 Javascript
Bootstrap BootstrapDialog使用详解
Feb 17 Javascript
angular1配合gulp和bower的使用教程
Jan 19 Javascript
利用js实现前后台传送Json的示例代码
Mar 29 Javascript
Vue无限滑动周选择日期的组件的示例代码
Jul 18 Javascript
BootStrap table实现表格行拖拽效果
Dec 01 Javascript
8 个有用的JS技巧(推荐)
Jul 03 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中上传多个文件的表单设计例子
2014/11/19 PHP
繁简字转换功能
2006/07/19 Javascript
Mootools 1.2教程 Fx.Morph、Fx选项和Fx事件
2009/09/15 Javascript
8个超棒的学习 jQuery 的网站 推荐收藏
2011/04/02 Javascript
jQuery针对各类元素操作基础教程
2014/08/29 Javascript
jQuery判断数组是否包含了指定的元素
2015/03/10 Javascript
ExtJs动态生成treepanel的Json格式
2015/07/19 Javascript
ES6中如何使用Set和WeakSet
2016/03/10 Javascript
javascript 用函数实现继承详解
2016/05/28 Javascript
原生Javascript插件开发实践
2017/01/09 Javascript
微信小程序去哪里找 小程序到底如何使用(附小程序名单)
2017/01/09 Javascript
详解Immutable及 React 中实践
2018/03/01 Javascript
Vue设置长时间未操作登录自动到期返回登录页
2020/01/22 Javascript
react ant Design手动设置表单的值操作
2020/10/31 Javascript
使用Python发送邮件附件以定时备份MySQL的教程
2015/04/25 Python
Python中isnumeric()方法的使用简介
2015/05/19 Python
python写日志封装类实例
2015/06/28 Python
在Django框架中设置语言偏好的教程
2015/07/27 Python
使用Python的Tornado框架实现一个Web端图书展示页面
2016/07/11 Python
Python使用win32com实现的模拟浏览器功能示例
2017/07/13 Python
python3.4下django集成使用xadmin后台的方法
2017/08/15 Python
纯python实现机器学习之kNN算法示例
2018/03/01 Python
python实现贪吃蛇游戏
2020/03/21 Python
python脚本实现音频m4a格式转成MP3格式的实例代码
2019/10/09 Python
如何使用scrapy中的ItemLoader提取数据
2020/09/30 Python
美国知名的女性服饰品牌:LOFT(洛芙特)
2016/08/05 全球购物
惊艳的手工时装首饰:Migonne Gavigan
2018/02/23 全球购物
运动鞋、足球鞋和慕尼黑球衣:Sport Münzinger
2019/08/26 全球购物
Moda Italia荷兰:意大利男士服装
2019/08/31 全球购物
本科生的职业生涯规划范文
2014/01/09 职场文书
工程质量承诺书
2014/03/27 职场文书
2014年班主任自我评价范文
2014/04/23 职场文书
本科毕业生应聘求职信
2014/07/06 职场文书
忠诚教育学习心得体会
2016/01/23 职场文书
2016年度师德标兵先进事迹材料
2016/02/26 职场文书
ssh服务器拒绝了密码 请再试一次已解决(亲测有效)
2022/08/14 Servers