微信H5自定义分享页面总结
微信公众号中默认的分页页面很难看,默认的缩略图。工作需要将他自定义为想要的形式。
具体详情查看微信开发者文档:微信开发者文档
第一步、公众号需要配置
1、需要一个公众号的 appId、appSecret 只要有公众号,那么这个就有。
2、在该公众号的js安全域名配置一个域名用来做分享出去的链接。(登录微信公众平台---->公众号设置---->功能设置---->js安全域名)。

3、需要开通微信分享接口(登录微信公众平台----> 接口权限 ---->分享接口)

第二步、后台准备
临时票据:jsapi_ticket
jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。
1.通过下面链接能够获取access_token(get请求)(有效期7200秒,开发者必须在自己的服务全局缓存access_token):
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
2.拿上面得到的access_token后,通过下面的链接,采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
成功返回如下JSON:
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
3、获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
签名算法
签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
即signature=sha1(string1)。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步骤2. 对string1进行sha1签名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事项:
1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。
2.签名用的url必须是调用JS接口页面的完整URL。
3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。
4.要注意签名算法是否正确-----确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验
总结一下就是:
先获取:access_token
再获取:jsapi_ticket
拼接字符串后进行sha1加密得到:signature
代码大致如下(仅供参考):
一、fileCache.php
<?php
class FileCache
{
/**
* 缓存目录
* @var
*/
private $cache_dir;
/**
* @param $cache_dir
* @throws Exception
*/
public function __construct($cache_dir)
{
$this->cache_dir = $cache_dir;
if (!is_dir($cache_dir)) {
$make_dir_result = mkdir($cache_dir, 0755, true);
if ($make_dir_result === false) throw new Exception('Cannot create the cache directory');
}
}
/**
* 根据key获取值,会判断是否过期
* @param $key
* @return mixed
*/
public function get($key)
{
$cache_data = $this->getItem($key);
if ($cache_data === false || !is_array($cache_data)) return false;
return $cache_data['data'];
}
/**
* 添加或覆盖一个key
* @param $key
* @param $value
* @param $expire
* @return mixed
*/
public function set($key, $value, $expire = 0)
{
return $this->setItem($key, $value, time(), $expire);
}
/**
* 设置包含元数据的信息
* @param $key
* @param $value
* @param $time
* @param $expire
* @return bool
*/
private function setItem($key, $value, $time, $expire)
{
$cache_file = $this->createCacheFile($key);
if ($cache_file === false) return false;
$cache_data = array('data' => $value, 'time' => $time, 'expire' => $expire);
$cache_data = json_encode($cache_data);
$put_result = file_put_contents($cache_file, $cache_data);
if ($put_result === false) return false;
return true;
}
/**
* 创建缓存文件
* @param $key
* @return bool|string
*/
private function createCacheFile($key)
{
$cache_file = $this->path($key);
if (!file_exists($cache_file)) {
$directory = dirname($cache_file);
if (!is_dir($directory)) {
$make_dir_result = mkdir($directory, 0755, true);
if ($make_dir_result === false) return false;
}
$create_result = touch($cache_file);
if ($create_result === false) return false;
}
return $cache_file;
}
/**
* 判断Key是否存在
* @param $key
* @return mixed
*/
public function has($key)
{
$value = $this->get($key);
if ($value === false) return false;
return true;
}
/**
* 加法递增
* @param $key
* @param int $value
* @return mixed
*/
public function increment($key, $value = 1)
{
$item = $this->getItem($key);
if ($item === false) {
$set_result = $this->set($key, $value);
if ($set_result === false) return false;
return $value;
}
$check_expire = $this->checkExpire($item);
if ($check_expire === false) return false;
$item['data'] += $value;
$result = $this->setItem($key, $item['data'], $item['time'], $item['expire']);
if ($result === false) return false;
return $item['data'];
}
/**
* 减法递增
* @param $key
* @param int $value
* @return mixed
*/
public function decrement($key, $value = 1)
{
$item = $this->getItem($key);
if ($item === false) {
$value = 0 - $value;
$set_result = $this->set($key, $value);
if ($set_result === false) return false;
return $value;
}
$check_expire = $this->checkExpire($item);
if ($check_expire === false) return false;
$item['data'] -= $value;
$result = $this->setItem($key, $item['data'], $item['time'], $item['expire']);
if ($result === false) return false;
return $item['data'];
}
/**
* 删除一个key,同时会删除缓存文件
* @param $key
* @return mixed
*/
public function delete($key)
{
$cache_file = $this->path($key);
if (file_exists($cache_file)) {
$unlink_result = unlink($cache_file);
if ($unlink_result === false) return false;
}
return true;
}
/**
* 清楚所有缓存
* @return mixed
*/
public function flush()
{
return $this->delTree($this->cache_dir);
}
/**
* 递归删除目录
* @param $dir
* @return bool
*/
function delTree($dir)
{
$files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $file) {
(is_dir("$dir/$file")) ? $this->delTree("$dir/$file") : unlink("$dir/$file");
}
return rmdir($dir);
}
/**
* 根据key获取缓存文件路径
*
* @param string $key
* @return string
*/
protected function path($key)
{
$parts = array_slice(str_split($hash = md5($key), 2), 0, 2);
return $this->cache_dir . '/' . implode('/', $parts) . '/' . $hash;
}
/**
* 获取含有元数据的信息
* @param $key
* @return bool|mixed|string
*/
protected function getItem($key)
{
$cache_file = $this->path($key);
if (!file_exists($cache_file) || !is_readable($cache_file)) {
return false;
}
$cache_data = file_get_contents($cache_file);
if (empty($cache_data)) return false;
$cache_data = json_decode($cache_data, true);
if ($cache_data) {
$check_expire = $this->checkExpire($cache_data);
if ($check_expire === false) {
$this->delete($key);
return false;
}
}
return $cache_data;
}
/**
* 检查key是否过期
* @param $cache_data
* @return bool
*/
protected function checkExpire($cache_data)
{
$time = time();
$is_expire = intval($cache_data['expire']) !== 0 && (intval($cache_data['time']) + intval($cache_data['expire']) < $time);
if ($is_expire) return false;
return true;
}
}
二、KktWxjs.php
<?php
header('Access-Control-Allow-Origin:*');//允许跨域
header('Access-Control-Allow-Methods:POST,GET');//允许的请求
header("Content-Type:application/json/json");
require_once("fileCache.php");
class wxjs{
private static $appId = "wxc*********21";
private static $appSecret = "4c7**************79b";
public function getTicekt(){
$ticket = '';
$diffTime = 0;
//现在的时间戳
$date = strtotime(date("Y-m-d H:i:s"));
//获取缓存信息
$cache= new FileCache('cache');
if($cache->has('jsapi_ticket')){
$ticket = $cache->get('jsapi_ticket');
$expiresIn = $cache->get('expires_in')-10;//过期时间
$create_time = $cache->get('create_time');//上次更新的时间
$diffTime = $date - strtotime($create_time);
}
if ($diffTime >= $expiresIn || !$ticket) {//判断是否过期
$appId = wxjs::$appId; //微信appid
$appSecret = wxjs::$appSecret; //微信密钥
// 参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token)
// https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" . $appId . "&secret=" . $appSecret; //获取token的url
$dataToken = wxjs::requestGet($url); //curl获取access_token
$token = $dataToken['access_token']; //新的access_token
$expires = $dataToken['expires_in']; //新的过期时间
// 用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):
// https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
$urlTicket = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" . $token . "&type=jsapi"; //获取ticket的url
$dataTicket = wxjs::requestGet($urlTicket);
$ticket = $dataTicket['ticket'];
// 服务全局缓存jsapi_ticket 、expires_in 、create_time
$cache->set("jsapi_ticket",$ticket);
$cache->set("expires_in",$expires);
$cache->set("create_time",$create_time = date("Y-m-d H:i:s"));
} else {
$ticket = $ticket;
}
return $ticket;
}
// 通过该接口获取access_token、和 jsapi_ticket
public static function requestGet($url = '')
{
if (empty($url)) {
return false;
}
$curl = curl_init(); //初始化
curl_setopt($curl, CURLOPT_URL, $url); //设置抓取的url
curl_setopt($curl, CURLOPT_HEADER, 0); //设置头文件的信息作为数据流输出
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); //设置获取的信息以文件流的形式返回,而不是直接输出。
$data = curl_exec($curl); //执行命令
curl_close($curl); //关闭URL请求
$data = json_decode($data, true);
return $data;
}
// 获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。
public function wxDataFormat($url){
$str="QWER*************7890";
$nonce = str_shuffle($str);//随机字符串
$timestamp = strtotime(date("Y-m-d H:i:s")); //现在的时间戳
$ticket = $this->getTicekt();
$str = "jsapi_ticket=".$ticket."&noncestr=".$nonce."×tamp=".$timestamp."&url=".$url;
$signature = sha1($str);
// echo $str;
$data = array("noncestr"=>$nonce,"timestamp"=>$timestamp,"signature"=>$signature);
return $data;
}
}
$poststr = file_get_contents("php://input");
$post = json_decode($poststr,true);
$url = $post['url'];
$wx = new wxjs();
$data = $wx->wxDataFormat($url);
$data = json_encode($data,true);
$response = '{"data":'.$data.',"statusMsg":"成功","statusCode":200}';
echo $response;
三、 第三步前端工作
通过接口获取配置信息 —用来签名 -----通过config接口注入权限验证配置
Axios.post("http://******/WXJsInfo.php", {
url: window.location.href.split("#")[0]
})
.then(res => {
let data = res.data.data; // PS: 这里根据你接口的返回值来使用
wx.config({
debug: false, // 开启调试模式
appId: "wx9********5cf", // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.noncestr, // 必填,生成签名的随机串
signature: data.signature, // 必填,签名,见附录1
jsApiList: [
"updateAppMessageShareData",
"updateTimelineShareData",
"onMenuShareAppMessage",
"onMenuShareTimeline",
"hideMenuItems"
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
}); 20.
})
2.通过wx.ready来检查签名是否成功
wx.ready(function(){
注意:
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});
3.检查是否用权利调用相应的接口
wx.checkJsApi({
jsApiList: ['chooseImage'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function(res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
}
});
完整代码:
wxShare.js
const wxShare = {
wxRegister(axiosUrl,title, desc, link, imgUrl) {
//axiosUrl:后端提供的链接,用来获取JS-SDK权限验证的签名等信息 这里就是http://*******/KktWxjs.php文件 (必须与当前页面同源)
//title:自定义名称
//des:自定义描述
//link:分享的链接(必须是js安全域名)
//imgUrl:图片链接
axios.post(axiosUrl, {
url: window.location.href
})
.then(res => {
let data = res.data.data; // PS: 这里根据你接口的返回值来使用{noncestr:"R7AG3**************YCVLPH2W8UI",signature:"765f0618f********82f7e2621d2e59c",timestamp:160***302}
wx.config({
debug: false, // 开启调试模式
appId: "wxcc617f1932e74a21", // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.noncestr, // 必填,生成签名的随机串
signature: data.signature, // 必填,签名,见附录1
jsApiList: [
"updateAppMessageShareData",
"updateTimelineShareData",
"onMenuShareAppMessage",
"onMenuShareTimeline",
"hideMenuItems"
] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
wx.checkJsApi({
jsApiList: ["onMenuShareAppMessage", "onMenuShareTimeline"], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function (res) {
// 以键值对的形式返回,可用的api值true,不可用为false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
console.log(res);
// 分享给好友
wx.onMenuShareAppMessage({
title: title,
desc: desc,
link: link, //必须和当前页面同源
imgUrl: imgUrl,
trigger: function () {
// 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
// alert("用户点击发送给朋友");
},
success: function () {
alert("分享成功");
that.hideMask = "none";
that.clientCount = 0;
that.requestClientCount(0);
},
cancel: function () {
alert("分享失败");
},
fail: function () {
alert("分享失败");
}
});
// 分享给朋友圈
wx.onMenuShareTimeline({
title: title,
link: link,
imgUrl: imgUrl,
trigger: function () {
// 不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回
// alert("用户点击发送给朋友");
},
success: function () {
alert("分享成功");
setTimeout(function () {
//回调要执行的代码
that.hideMask = "none";
that.clientCount = 0;
that.requestClientCount(0);
}, 500);
},
cancel: function () {
alert("分享失败");
},
fail: function () {
alert("分享失败");
}
});
}
});
//签名成功后调用的方法
wx.ready(function () {
//需在用户可能点击分享按钮前就先调用
// alert("签名成功");
});
// 签名失败调用的接口
wx.error(function () {
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
alert("签名失败");
});
})
.catch(error => {
console.log("配置出错", error);
});
}
};
调用
<script src="http://******/wxShare/weixin-js-sdk.js"></script>
<script src="http://******/wxShare/axios.js"></script>
<script src="http://******/wxShare/wxShare.js"></script>
//自定义微信分享
wxShare.wxRegister('http://******/KktWxjs.php','自定义名称','自定义描述','自定义链接',"自定义图片链接");
2181

被折叠的 条评论
为什么被折叠?



