vue+node weixin-js-sdk分享给朋友及分享到朋友圈,内容设置。

812 阅读5分钟

1.介绍

需求描述:app中的h5页面有两个按钮分别为分享给朋友,及分享到朋友圈,分享样式自定义。

方向:使用weixin-js-sdk。

结果:h5无法完成分享到给朋友圈,及分享到朋友圈,需app端配合建立微信桥。

原因:weixin-js-sdk的分享给朋友及分享到朋友圈作用仅为设置分享的内容,并没有分享的行为。需要在微信中点右上角的三个点手动分享。

2.weixin-js-sdk

了解了weixin-js-sdk在分享功能上能做什么之后,先看一下官方文档的使用步骤准备开始使用weixin-js-sdk编辑分享内容。

先看这么多,我们首先需要设置JS接口安全域名及对wx进行配置,个人认为对前端开发来讲,在开发过程中比较不容易弄的两个点就是步骤一及步骤三的配置。

原因:

  • 步骤一:绑定JS接口安全域名的时候,需要域名通过ICP备案和将指定文件放入web服务器中。
  • 步骤三:配置中的签名需要根据指定规则去进行加密且还有生成的时间及随机串等,一般应该后台同学来做。 解决:
  • 步骤一:在没有域名时,可以采取使用测试微信公众号方式,利用内网穿透去进行设置JS安全域名,测试微信公众号的要求少一点。
  • 步骤三:使用nodeJs模拟后台同学,根据文档去进行生成签名及随机串生成签名的时间戳等返回到前端。

3.开始动手前的准备工作

3.1 内网穿透

使用内网穿透可以获得临时域名,可以在微信中打开我们的页面方便测试,并设置JS接口安全域名,如有服务器及域名不需要内网穿透。

  • ngrok.com 国外 注册既使用,但大部分网页在微信中因被举报无法打开。
  • natapp.cn 国内 需实名认证,免费隧道慢,有收费隧道。

3.2 申请测试微信公众号

申请微信测试公众号地址
mp.weixin.qq.com/debug/cgi-b…
申请测号的目的是为了获得appID和appsecret生成标签,设置JS接口安全域名,如果没有服务器及域名,需要使用测试微信公众号。因为使用正式公众号需要在设置JS接口安全域名时,域名需通过ICP备案,及将指定文件放入web服务器中。

4. 根据文档一步一步来使用nodeJs生成签名 JS-SDK使用权限签名算法

JS-SDK使用权限签名算法文档: developers.weixin.qq.com/doc/offiacc…
文档中说要生成签名需要jsapi_ticket,它是公众号调用微信js接口的临时票据,要获得jsapi_ticket又需要access_token。


首先准备,设置缓存与获取缓存方法然后开始。

const NodeCache = require("node-cache");
const myCache = new NodeCache({
  stdTTL: 7200, // 缓存过期时间
  checkperiod: 120 // 定期检查时间
});

4.1.获取access_token

4.1.1接口调用请求说明
 请求方式GET:
 https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET  
 请求参数: APPID,APPSECRET,测试公众号中获取
4.1.2返回响应
{"access_token":"ACCESS_TOKEN","expires_in":7200}
说明:
access_token:获取到的凭证
expires_in: 有效时间
4.1.3 node请求获取access_token
// 使用axios进行get请求
const axios = require('axios');
const appId = '***';//微信测试公众号中获取
const secret = '***';//同上

/**
 * @description 获取access_token
 */
const getAccess_token = () => {
  // 先查看缓存有没有
  if (myCache.get('access_token')) {
    jsapi_ticket(); // 获取jsapi_ticket方法
  } else {
    axios.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${secret}`).then(async (res) => {
      if (res.data.access_token) {
        // 将access_token存入缓存中
        myCache.set('access_token', res.data.access_token)
        await jsapi_ticket();
      }
    }).catch(err => {
      console.log('axios occurs ', err);
    });
  }
}

执行结果成功获取到access_token

获取到access_token后进行下一步操作,去获取jsapi_ticket,如返回非以上结果,请查询官网文档。


4.2 获取jsapi_ticket

4.2.1 文档

根据官网指导用第一步拿到的access_token后去获得jsapi_ticket。

文档地址 : developers.weixin.qq.com/doc/offiacc…

4.2.1 请求地址
请求方式GET
地址: https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi  
ACCESS_TOKEN:为之前请求结果中的access_token
4.2.2返回响应
{
  "errcode":0,
  "errmsg":"ok",
  "ticket":"xLdikRXVbTPdHSM05e5u5sUoXNKd841ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
  "expires_in":7200
}
//ticket就是jsapi_ticket。其他参数不做过多解释。
4.2.3node中获取jsapi_ticket。
/**
 * 
 * @description 获取jsapi_ticket 
 */
const jsapi_ticket = async () => {
  const access_token = myCache.get('access_token');
  axios.get(`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`).then(async res => {
    // 判断缓存是否有ticket。
    if (myCache.get('ticket')) {
      getSign()
    } else {
      // 将ticket存入缓存中
      myCache.set('ticket', res.data.ticket)
      await getSign();
    }
  }).catch(err => {
    console.log('axios occurs ', err);
  });
}

执行结果

获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。


4.3 签名算法

4.3.1文档

文档地址: developers.weixin.qq.com/doc/offiacc…
JS接口校验: mp.weixin.qq.com/debug/cgi-b…

4.3.2 整理文档生成签名的步骤

从文档中得到我们需要的几个参数然后生成签名:

序号参数说明
1noncestr随机字符串
2有效的jsapi_ticket从上述接口获得
3timestamp时间戳
4url当前网页的URL不包含#及其后面部分
5排序对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
6拼接使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1参数需小写
7加密得到签名对string1进行sha1算法加密 (js-sha1包提供的功能),得到signature
4.3.3 noncestr随机字符串
/**
 * 生成签名的随机串
 * @return {随机字符串}
 */
let createNonceStr = () => {
  return Math.random().toString(36).substr(2, 15)
}
4.3.4有效的jsapi_ticket
myCache.get('ticket');
4.3.5 timestamp 时间戳
/**
 * 生成签名的时间戳
 * @return {时间戳}
 */
let createTimestamp = () => {
  return parseInt(new Date().getTime() / 1000) + ''
}
4.3.5 当前网页的URl
// 通过前端传入存进缓存
myCache.get('url')
4.3.6 将所有待签名参数排序(字典序从小到大)

所有待签名参数有noncestr、jsapi_ticket、timestamp、url

/**
 * 对参数对象进行字典排序
 * @param  {对象} args 签名所需参数对象
 */
let dictionarySort = (args) => {
  var keys = Object.keys(args)
  keys = keys.sort()

  var newObjs = {}
  keys.forEach(function (key) {
    newObjs[key.toLowerCase()] = args[key]
  })
  return strJoin(newObjs)
  }
4.3.7 拼接,将所有参数字典排序后拼接
/**
 * 拼接
 * @param  {对象} obj 排序后的参数
 */
let strJoin = (obj) => {
  var string = ''
  for (var i in obj) {
    string += '&' + i + '=' + obj[i]
  }
  string = string.substr(1)
  return string
}

排序和拼接其实可以合并,也可以用一个偷懒的方式去做,手动拼接

let lazyMode =  (ticket, noncestr, ts, url) => {
          const str = 'jsapi_ticket=' + ticket + '&noncestr=' + noncestr + '&timestamp='+ ts +'&url=' + url;
          return str
}
4.3.8加密生成签名
const crypto = require('crypto');

// sha1加密
let sha1 = (str) => {
  let shasum = crypto.createHash("sha1")
  shasum.update(str)
  str = shasum.digest("hex")
  return str // 返回加密后的签名。
}
4.3.9 整理数据 返回到前端
const express = require('express');
const app = express();
const bodyParser = require('body-parser');

/**
 * 
 * @description 整理数据返回到前端
 * @param params.url 当前网页
 * @param params.ticket ticket
 * @return {allData} 全部数据
 * jsapi_ticket 生成的签名
 * nonceStr  随机字符串
 * timestamp 时间戳
 * url       当前网页
 * appId     appid
 * signature 签名
 */
const getSign = () => {
  const res = {
    jsapi_ticket: myCache.get('ticket'),
    nonceStr: createNonceStr(),
    timestamp: createTimestamp(),
    url: myCache.get('url')
  };
  const string = dictionarySort(res); // 将所有参数排序
  res.signature = sha1(string); // 对排序的参数加密  
  res.appId = appId; // 增加appId 前端需要
  return res
}

app.post('/sign', async (req, res) => {
  // 将url存入缓存
  myCache.set('url', req.body.url);
  await getAccess_token();

  res.send({
    code: 0,
    data: getSign()
  })
});


app.listen(8888, () => console.log(`Example app listening on port ${8888}!`))

至此,node暂时结束。

5.前端部分

前端部分用的vue

  created() {
  	// 调用接口,url为当前网页,这里路由用的history模式,所以暂时没有#号,没有做任何处理
        this.testAction({url: window.location.href}).then((res) => {
            wx.config({
                debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                appId: res.appId, // 必填,公众号的唯一标识,填自己的!
                timestamp: res.timestamp, // 必填,生成签名的时间戳,刚才接口拿到的数据
                nonceStr: res.nonceStr, // 必填,生成签名的随机串
                signature: res.signature, // 必填,签名,见附录1
                jsApiList: ['onMenuShareAppMessage', 'onMenuShareTimeline'] // 所需的weixin-js-api
            });
            wx.ready(function() {
                wx.checkJsApi({
                    jsApiList: ['onMenuShareAppMessage'],
                    success: function(res: any) {}
                });
            });
            wx.onMenuShareAppMessage({
                title: '分享朋友',
                desc: '分享朋友123',
                link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: '', // 分享图标
                success: function() {},
                cancel: function() {}
            });
            wx.onMenuShareTimeline({
                title: '分享到朋友圈', // 分享标题
                link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
                imgUrl: '', // 分享图标
                success: function() {
                    // 用户点击了分享后执行的回调函数
                }
            });
        });
    }

至此,前端部分结束。可以根据业务需求修改编辑内容的位置。
在微信中打开网页进行测试,点击微信中的右上角三个点去分享。

总结

1.该版本node部分还需完善,每天的接口请求次数有限所以需要注意存放,建议放到数据库中。
2.H5的weixin-js-sdk没有分享动作的能力,只是编辑出去的内容,如果安卓及ios端有修改内容,那么H5端的数据会失效。
3.注意调用给后台传url时的#,及分享的链接域名要与设置的安全域名一致。
4.欢迎各位互相交流经验。