微信JS-SDK使用分享功能

编程

微信为微信客户端的网页开发提供了一个 SDK 完成诸如 微信生态内分享,身份认证登录,调用微信能力的功能。但是,这个 SDK 随着微信政策的变化,已经产生了一些变化。

实现朋友圈分享个性化

很多网页在分享在朋友圈的时候是一个卡片的形式,但是,如果你不调用微信 SDK 的话,你的分享就是一个干巴巴的链接和一个默认缺失的图片。

微信分享到朋友圈功能需要您有一个认证过的非个人公众号,并且该域名在公众号的 JS 安全域名之内

近几年,微信的政策越来越卡紧,很多变化并未在文档中说出来,只能依靠开发者自己摸索….有点无语哈。比如这个分享功能,目前来说,尽管你使用了微信 SDK ,您也必须从公众号内打开链接,点击右上角的三个点点,选择分享到朋友圈,才能成功触发分享逻辑,得到一个卡片链接的形式。否则分享将是以文本链接的形式发送

这个机制的存在导致如果您想将链接以卡片分享朋友圈或分享给朋友,你必须通过公众号发文,然后打开,然后转发。当然,更简单的做法是,将该链接发送到任意一个公众号的聊天窗口,然后点击该链接,选择分享。


流程:

完成这一操作:

  1. 绑定域名

    先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。

    备注:登录后可在“开发者中心”查看对应的接口权限。

  2. 引入 JS-SDK

  3. 通过 config 接口注入权限验证配置

    这一步需要配合后端进行签名验证

  4. 通过 ready 接口处理成功验证。

    等待用户点击分享,(当然,用户可以点击网页内按钮触发分享操作,不过这个特性在文档中被标记为很快将被移除且很容易触犯微信的禁止诱导分享条款


客户端网页

  1. 在网页中加入以下代码引入 SDK

    1
    <script type="text/javascript" charset="utf-8" src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  2. 在网页中配置微信 SDK ,一般发生在网页打开时进行操作。

    1
    2
    3
    4
    // 判断是否微信客户端,在客户端打开才进行配置操作
    if (is_weixin()) {
    wxShareInit();
    }
  3. 需要先调用 wx.config() 注入权限验证

    1
    2
    3
    4
    5
    6
    7
    8
    wx.config({
    debug: true, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名
    jsApiList: [] // 必填,需要使用的 JS 接口列表
    });

    这里的 appId 是你的公众号 appId

    timestamp , nonceStr , signature 需要请求后端生成,微信官方没有提供后端示例代码,需要自己编写接口。注意:该后端的 ip 地址需要添加在公众号的 白名单列表中 。关于如何得到这些参数,我将在后面说明。

    jsApiList 是你这个页面需要用到的 api 列表。本次我们只实现分享功能,所以,只需要两个 api : ‘updateAppMessageShareData’, ‘updateTimelineShareData’ 第一个是分享给朋友的 api ,第二个是分享到朋友圈。

  4. 调用分享 API

    注入完权限以后,我们需要调用上面的两个 api 初始化分享工作,以便在用户选择分享的时候,提供正确的卡片信息。当然,如果,你需要在网页一开始载入完成之后就调用这两个接口,那么你可能需要监听 wx.config() 是否完成。

    不过,微信提供了 ready 函数,微信将在 wx.config() 完成之后,调用该函数,所以,你可以把函数写在 ready 里面。

    like this:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    wx.ready(function () {   //需在用户可能点击分享按钮前就先调用
    wx.updateAppMessageShareData({
    title: 'title', // 分享标题
    desc: 'descriipt', // 分享描述
    link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
    imgUrl: '', // 分享图标
    success: function () {
    // 设置成功
    console.log('appMessageShareData set successful!');
    }
    })
    //需在用户可能点击分享按钮前就先调用
    wx.updateTimelineShareData({
    title: '', // 分享标题
    link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
    imgUrl: '', // 分享图标
    success: function () {
    // 设置成功
    console.log('appTimeLineShareData set successful!');
    }
    })
    });
  5. enjoy it

    现在,我们已经完成了所有操作,在用户点击分享时,将出现定义的信息卡片。


生成签名

不过还有最重要的一步,生成那一串签名参数!
你需要在 js 中请求你的后端服务器,服务器返回这些数据后,再将它们放到 wx.config() 里面调用 wx.config()

关于后端的代码,我们可以用 PHP 写一个小小的 demo,再次强调,后端服务器的 ip 地址,需要在你的公众号 ip 白名单中。

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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<?php
$store = require('store.php');
//允许跨域
header('Access-Control-Allow-Origin: *');
header('Content-Type: text/plain; charset=utf-8');

//需要配置的公众号信息
$appId = '';
$appSecret = '';
$urlwhitelist = array('example.com');
// the urls allow

//存储
$storeFile = 'store.php';

//微信文档地址配置
$tokenUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s';
$ticketUrl = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi';
$nonceStr = 'aRandomStr';

/*
从微信获取token 有效默认7200ms 需要存储 不能一直刷新 否则会被屏蔽
*/
function getAccessTokenFromWx() {
global $tokenUrl, $appId, $appSecret;
$url = sprintf($tokenUrl, $appId, $appSecret);
$data = curl_get_https($url);
return $data['access_token'];
}

/*
从微信获取ticket 有效默认7200ms 需要存储 不能一直刷新 否则会被屏蔽
*/
function getTicketFromWx($token) {
global $ticketUrl;
$url = sprintf($ticketUrl, $token);
$data = curl_get_https($url);
return $data;
}

/*
存储ticket
*/
function storeTicket($ticket, $expires_in) {
global $storeFile;
$newConfig = '<?php return array("ticket" => "' . $ticket . '","expires_in" => "' . $expires_in . '");';
file_put_contents($storeFile, $newConfig);
}

function getTicket($authUrl) {
global $store, $appId, $nonceStr;
//当前时间戳
$timestamp = time();
//现从本地获取看是否过期
$ticket = $store['ticket'];
if (intval($store['expires_in']) > $timestamp) {
//未过期
$ticket = $store['ticket'];
} else {
//先微信获取
$token = getAccessTokenFromWx();
$ticketData = getTicketFromWx($token);
//取出数据
$ticket = $ticketData['ticket'];
$expires_in = intval($ticketData['expires_in']) + $timestamp;
//存储到本地
storeTicket($ticket, $expires_in);
}

//签名
$signature = getSignature($ticket, $nonceStr, $timestamp, $authUrl);
$res = array(
'appId' => $appId,
'timestamp' => $timestamp,
'nonceStr' => $nonceStr,
'signature' => $signature,
'ticket' => $ticket,
'url' => $authUrl,
'code' => 0
); //【关联数组】

return json_encode($res);
}
function getSignature($jsapi_ticket, $nonceStr, $timestamp, $url) {
$preSign = 'jsapi_ticket='.$jsapi_ticket.'&noncestr='.$nonceStr.'&timestamp='.$timestamp.'&url='.$url;
$rtn = sha1($preSign);
return $rtn;
}

//https请求
function curl_get_https($url) {
$curl = curl_init(); // 启动一个CURL会话
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, true); // 从证书中检查SSL加密算法是否存在
$tmpInfo = curl_exec($curl); //返回api的json对象
//关闭URL请求
curl_close($curl);
$jsonData = json_decode($tmpInfo, true);
//errcode
if (isset($jsonData['errcode']) && $jsonData['errcode'] != 0) {
echo json_encode(array(
'code' => $jsonData['errcode'],
'msg' => $jsonData['errmsg']
));
exit(1);
}

return $jsonData;
}

if (is_array($_GET) && count($_GET) > 0) {
if (isset($_GET["url"])) {
$url = $_GET["url"];
if (in_array(parse_url($url)['host'], $urlwhitelist)){
echo getTicket($url);
}else{
die(json_encode(array(
'code'=> -1,
'msg'=> 'haha'
)));
}
return;
}
}

echo json_encode(array(
'code' => 1,
'msg' => 'error'
));

在以上代码中配置好 appid , secrectKeyurlwhitelist 之后,将他放到你的后端服务器,以 get 参数传入 url,它将会返回类似下面的 json 结果

1
2
3
4
5
6
7
8
9
{
"appId": "wxd5xxxxxxxxxx6a6",
"timestamp": 1659430417,
"nonceStr": "aRandomStr",
"signature": "e5652f0cc37c47dd2634b650ef4ea322ac08a862",
"ticket": "LIKLckvwlJT9cWIhEQTwfI0GEfJQc8xOZTcBjiE1Vetz5TdArzDaoeCy3EFBKzQxERPvrU0LVYFkFw-8A3HAlw",
"url": "http://example.com/index.php",
"code": 0
}

overall

好了,现在你的整个代码看起来应该是这样的

example.com/index.html

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
<html>
<head>
... other codes
<script type="text/javascript" charset="utf-8" src="http://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
</head>
.... other codes

<script>
.... other codes
// 根据 UA 判断是否微信内
function is_weixin() {
var ua = navigator.userAgent.toLowerCase();
console.log('ua', ua);
if (ua.match(/MicroMessenger/i) == "micromessenger") {
return true;
} else {
return false;
}
}

function wxShareInit() {
data = false;
let xmlhttp = new XMLHttpRequest();
// 从后端服务器请求 签名 ,传入当前 url
xmlhttp.open('get', 'http://yourserve.com/sig.php?url=' + window.location.href, true);
xmlhttp.send();
xmlhttp.onreadystatechange = function () {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// console.log(xmlhttp.response);
data = JSON.parse(xmlhttp.response);
console.log(data);
if (!data || 0 != data.code) {
console.log("get ticket fail!");
return;
}
// 将请求得到的 签名放进 config 注入权限验证
wx.config({
debug: false, // 开启调试模式,调用的所有 api 的返回值会在客户端 alert 出来,若要查看传入的参数,可以在 pc 端打开,参数信息会通过 log 打出,仅在 pc 端时才会打印。
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature,// 必填,签名
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 必填,需要使用的 JS 接口列表
});

// 注入权限验证完成以后,微信会调用 ready ,将 api 写在里面调用。
wx.ready(function () { //需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({
title: 'title', // 分享标题
desc: 'descriipt', // 分享描述
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl: '', // 分享图标
success: function () {
// 设置成功
console.log('appMessageShareData set successful!');
}
})
//需在用户可能点击分享按钮前就先调用
wx.updateTimelineShareData({
title: '', // 分享标题
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号 JS 安全域名一致
imgUrl: '', // 分享图标
success: function () {
// 设置成功
console.log('appTimeLineShareData set successful!');
}
})
});
} else if (xmlhttp.readyState == 4 && xmlhttp.status != 200) {
console.Error("网络错误,请稍后重试");
}
}
}

if (is_weixin()) {
wxShareInit();
}
</script>
....
</html>

Really Enjoy it!

现在打开网页,点击分享,如果没有达到预期效果,可能是微信缓存的锅,不过,把锅甩给缓存之前,你最好将 wx.config() 的 debug 参数设为 true ,以检查所有操作是否都已正确完成。

Author: 哒琳

Permalink: http://blog.jieis.cn/2022/915f8a5b-2b54-4551-94b0-a471ba0f3f1c.html

Comments