解析微信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版Tab标签切换
Mar 16 Javascript
jQuery创建平滑的页面滚动(顶部或底部)
Feb 26 Javascript
Extjs中RowExpander控件的默认展开问题示例探讨
Jan 24 Javascript
JavaScript怎么判断图片是否加载完成以便获取其尺寸
May 08 Javascript
JavaScript实现16进制颜色值转RGB的方法
Feb 09 Javascript
jQuery对JSON数据进行排序输出的方法
Jun 24 Javascript
使用Promise解决多层异步调用的简单学习心得
May 17 Javascript
运用js教你轻松制作html音乐播放器
Apr 17 Javascript
JS+canvas实现的五子棋游戏【人机大战版】
Jul 19 Javascript
iview在vue-cli3如何按需加载的方法
Oct 31 Javascript
微信小程序 腾讯地图SDK 获取当前地址实现解析
Aug 12 Javascript
VUE中使用HTTP库Axios方法详解
Feb 05 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
一个域名查询的程序
2006/10/09 PHP
vBulletin HACK----显示话题大小和打开新窗口于论坛索引页
2006/10/09 PHP
PHP学习之整理字符串
2011/04/17 PHP
ThinkPHP模板比较标签用法详解
2014/06/30 PHP
PHP以mysqli方式连接类完整代码实例
2014/07/15 PHP
php生成随机颜色方法汇总
2014/12/03 PHP
php实现读取手机客户端浏览器的类
2015/01/09 PHP
双冒号 ::在PHP中的使用情况
2015/11/05 PHP
PHP利用Socket获取网站的SSL证书与公钥
2017/06/18 PHP
关于PHP转换超过2038年日期出错的问题解决
2017/06/28 PHP
laravel 5.4中实现无限级分类的方法示例
2017/07/27 PHP
thinkphp5实现无限级分类
2019/02/18 PHP
js 效率组装字符串 StringBuffer
2009/12/23 Javascript
JavaScript 学习笔记之操作符
2015/01/14 Javascript
bootstrap模态框垂直居中效果
2016/12/03 Javascript
javascript实现圣旨卷轴展开效果(代码分享)
2017/03/23 Javascript
关于angular js_$watch监控属性和对象详解
2017/04/24 Javascript
ES6中的Promise代码详解
2017/10/09 Javascript
基于jQuery Ajax实现下拉框无刷新联动
2017/12/06 jQuery
微信小程序在地图选择地址并返回经纬度简单示例
2018/12/03 Javascript
详解微信小程序scroll-view横向滚动的实践踩坑及隐藏其滚动条的实现
2019/03/14 Javascript
[03:48]2014DOTA2 TI专访71DK夺冠不靠小组赛高排名
2014/07/11 DOTA
[04:16]DOTA2全国高校联赛16强抽签
2018/05/02 DOTA
python模拟鼠标拖动操作的方法
2015/03/11 Python
python获取图片颜色信息的方法
2015/03/18 Python
Python利用BeautifulSoup解析Html的方法示例
2017/07/30 Python
Python读取MRI并显示为灰度图像实例代码
2018/01/03 Python
这可能是最好玩的python GUI入门实例(推荐)
2019/07/19 Python
Python 3.8 新功能全解
2019/07/25 Python
python实现网站用户名密码自动登录功能
2019/08/09 Python
python实现查找所有程序的安装信息
2020/02/18 Python
澳大利亚家庭花园和DIY工具网店:VidaXL
2019/05/03 全球购物
财政局长自荐信范文
2013/12/22 职场文书
人事任命通知
2015/04/20 职场文书
Mysql Innodb存储引擎之索引与算法
2022/02/15 MySQL
Python中非常使用的6种基本变量的操作与技巧
2022/03/22 Python