解析微信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 相关文章推荐
在IE模态窗口中自由查看HTML源码的方法
Mar 08 Javascript
JavaScript二维数组实现的省市联动菜单
May 08 Javascript
JavaScript 学习笔记之操作符(续)
Jan 14 Javascript
javascript结合Canvas 实现简易的圆形时钟
Mar 11 Javascript
jQuery使用attr()方法同时设置多个属性值用法实例
Mar 26 Javascript
使用AngularJS对路由进行安全性处理的方法
Jun 18 Javascript
简单谈谈JavaScript的同步与异步
Dec 31 Javascript
AngularJS出现$http异步后台无法获取请求参数问题的解决方法
Nov 03 Javascript
原生js实现中奖信息无间隙滚动效果
Jan 18 Javascript
360doc网站不登录就无法复制内容的解决方法
Jan 27 Javascript
通过实例解析JavaScript for in及for of区别
Jun 15 Javascript
vue 给数组添加新对象并赋值
Apr 20 Vue.js
利用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新手上路(十)
2006/10/09 PHP
PHP中常用数组处理方法实例分析
2008/08/30 PHP
详解PHP导入导出CSV文件
2014/11/03 PHP
php实现购物车功能(以大苹果购物网为例)
2017/03/09 PHP
php7基于递归实现删除空文件夹的方法示例
2017/06/15 PHP
js拖拽一些常见的思路方法整理
2014/03/19 Javascript
Javascript学习笔记之 函数篇(一) : 函数声明和函数表达式
2014/06/24 Javascript
js老生常谈之this,constructor ,prototype全面解析
2016/04/05 Javascript
jQuery Ajax 异步加载显示等待效果代码分享
2016/08/01 Javascript
Bootstrap页面缩小变形的快速解决办法
2017/02/03 Javascript
Nginx 配置多站点vhost 的方法
2018/01/07 Javascript
JS Web Flex弹性盒子模型代码实例
2020/03/10 Javascript
JS+CSS实现过渡特效
2021/01/02 Javascript
[13:18]《一刀刀一天》之DOTA全时刻21:详解TI新赛制 A队再露獠牙
2014/06/24 DOTA
[00:32]2016完美“圣”典风云人物:Maybe宣传片
2016/12/05 DOTA
python发送邮件示例(支持中文邮件标题)
2014/02/16 Python
python使用PIL缩放网络图片并保存的方法
2015/04/24 Python
python学习笔记之调用eval函数出现invalid syntax错误问题
2015/10/18 Python
Python编程之字符串模板(Template)用法实例分析
2017/07/22 Python
python对配置文件.ini进行增删改查操作的方法示例
2017/07/28 Python
Python跳出多重循环的方法示例
2019/07/03 Python
Python对wav文件的重采样实例
2020/02/25 Python
在django中查询获取数据,get, filter,all(),values()操作
2020/08/09 Python
python3.7中安装paddleocr及paddlepaddle包的多种方法
2020/11/27 Python
澳大利亚香水在线:Price Rite Mart
2017/12/28 全球购物
德国购买门票网站:ADticket.de
2019/10/31 全球购物
Booking.com德国:预订最好的酒店和住宿
2020/02/16 全球购物
亿阳信通股份有限公司笔试题(C#)
2016/03/04 面试题
情况说明书格式范文
2014/05/06 职场文书
幼儿园教师师德师风演讲稿:我自豪我是一名幼师
2014/09/10 职场文书
授权委托书怎么写
2014/09/25 职场文书
库房管理员岗位职责
2015/02/12 职场文书
2015新生加入学生会自荐书
2015/03/24 职场文书
唐山大地震观后感
2015/06/05 职场文书
2016学校元旦晚会经典开场白台词
2015/12/03 职场文书
创业计划书之珠宝饰品
2019/08/26 职场文书