微信分享效果

此类文档网上很多,bug解决了小半天,踩坑之后记录下来.

原理

原理是接入微信js-api,步骤如下:

  1. secret以及appid换取access_tooken(在服务器保存,有效期7200秒)

  2. access_tooken换取jsapi_ticket(在服务器保存,有效期7200秒)

  3. jsapi_ticket在前端页面进行初始化以及参数准备

其中1和2采用方式: 心跳node-schedule库
存储方式:fs文件写入(如果需要可以mysql存储)

目的

实现以前分享只显示自动抓取的网站标题和网址,图片默认第一张
实现以后可以自定义分享的标题,简介以及图片,并且可以使用回调

心跳换取access_tookenjsapi_ticket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const request = require(`request`);
const schedule = require('node-schedule');
const fs = require(`fs`);
const path = require(`path`);
const moment = require(`moment`);

const appid = `appid`;
const secret = `secret`;

//获取access_token
let access_token = ``;
let jsapi_ticket = ``;
let timestamp = moment().unix();
//定义函数,两步请求得到access_token以及jsapi_ticket
const writeFile = () => {
request.get(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appid}&secret=${secret}`,(err,response,body)=>{
if(err){
return console.log(err);
}else{
console.log('获取access_token之body' , body );
access_token = JSON.parse(body).access_token;
console.log('access_token',access_token);
//获取
request.get(`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token}&type=jsapi`,(err,response,body)=>{
if(err){
return console.log(err);
}else{
console.log('获取获取jsapi_ticket之body' , body );
jsapi_ticket = JSON.parse(body).ticket;
console.log('获取jsapi_ticket',jsapi_ticket);
//存储
let ready_data = {};
ready_data.access_token = access_token?access_token:'';
ready_data.jsapi_ticket = jsapi_ticket?jsapi_ticket:'';
ready_data.timestamp = timestamp?timestamp:'';
fs.writeFile(path.join(__dirname, 'access_token_ticket.txt'), JSON.stringify(ready_data) , function (err) {
if (err) {
return console.log(err);
}else{
console.log("写入文件成功");
}
});
}
});
}
});
}

writeFile();

var rule = new schedule.RecurrenceRule();
rule.hour = [0,2,4,6,8,10,12,14,16,18,20,22];
rule.minute = 0;
rule.second = 0;
var j = schedule.scheduleJob(rule, function(){
console.log('现在时间:',new Date());
writeFile();
});

服务器用来提供jsapi_ticket的接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//获取微信分享相关
const crypto = require(`crypto`);
const uuid = require(`uuid`);
const appid = `appid`;
const fs = require(`fs`);
const path = require(`path`);

let access_token = ``;
let jsapi_ticket = ``;
let timestamp;
router.post(`/getwxtooken`,async (req,res,next)=>{
await fs.readFile(path.join('/usr/local/workspaceThomas/getwxtookenforxntywebsite/', 'access_token_ticket.txt'),{encoding:'utf-8'}, async function(err,bytesRead) {
if (err) {
console.log( err );
};
var data = await JSON.parse(bytesRead);
access_token = await data?data.access_token:data.access_token;
jsapi_ticket = await data?data.jsapi_ticket:data.jsapi_ticket;
timestamp = await data?data.timestamp:parseInt(data.timestamp);
});

let noncestr = uuid.v1();//随机字符串
let url = req.body.url;
let string1 = await `jsapi_ticket=${jsapi_ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`;
var sha1 = await crypto.createHash("sha1"); //定义加密方式:md5不可逆,此处的md5可以换成任意hash加密的方法名称;
await sha1.update(string1);
var signature = await sha1.digest("hex"); //加密后的值

res.send({
appid: appid,
access_token: access_token,
noncestr: noncestr,
jsapi_ticket: jsapi_ticket,
timestamp: timestamp,
url: url,
string1: string1,
signature: signature
});

});

这里需要注意的是加密时的字符串拼接是由顺序的ASCII从小到大顺序,官方文档有相关说明
如果提示签名非法请按照官方文档从上至下排查
建议将async和await替换成Promise

前端进行的操作

先引入官方js

1
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
$(function() {
//微信分享
$.ajax({
"url":"jszhai.cn", //接口地址
"type":"post",
"dataType":"json",
"data":{"url": location.href.split('#')[0]},
"success":function(data){
wxstart(data, "");
console.log( data );
}
});
});

function wxstart(data, url){
var url = location.href.split('#')[0]; //分享的文章地址
var appId = data.appid;
var timestamp = data.timestamp;
var nonceStr = data.noncestr;
var signature = data.signature;
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId, // 必填,公众号的唯一标识
timestamp: timestamp, // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串,驼峰
signature: signature,// 必填,签名,见附录1
jsApiList: ["onMenuShareTimeline", "onMenuShareAppMessage", "onMenuShareQQ"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});

wx.ready(function(){
var imgurl = $('.art_titu').find('img').attr('src');//'https://' + window.location.hostname + '/static/customerHtml/img/weixinshare.jpg';
var title = document.title;
var desc = $('.art_main').text().replace(/\s+/g,'');;
wx.onMenuShareTimeline({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgurl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
alert('分享到朋友圈成功!');
},
cancel: function () {
// 用户取消分享后执行的回调函数
alert('您已取消分享到朋友圈!');
}
});

wx.onMenuShareAppMessage({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgurl, // 分享图标
type: '', // 分享类型,music、video或link,不填默认为link
dataUrl: '', // 如果type是music或video,则要提供数据链接,默认为空
success: function () {
// 用户确认分享后执行的回调函数
alert('分享给微信好友成功!');
},
cancel: function () {
// 用户取消分享后执行的回调函数
alert('取消分享成功!');
}
});

wx.onMenuShareQQ({
title: title, // 分享标题
desc: desc, // 分享描述
link: url, // 分享链接
imgUrl: imgurl, // 分享图标
success: function () {
// 用户确认分享后执行的回调函数
alert('成功分享给QQ好友!');
},
cancel: function () {
// 用户取消分享后执行的回调函数
alert('取消分享到QQ成功!');
}
});
});
}