微信公众号链接自定义分享总结

微信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&timestamp=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."&timestamp=".$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','自定义名称','自定义描述','自定义链接',"自定义图片链接");
### 微信公众号自定义菜单开发教程 #### 获取访问令牌 为了调用微信接口,首先需要获取`access_token`。这是微信公众平台提供的全局唯一票据,用于后续API请求的身份验证。 ```java // Java示例:获取 access_token 的 URL 构建方式 String tokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; ``` #### 创建自定义菜单结构体 构建要发送给微信服务器的JSON数据包,描述菜单项及其链接地址等内容。注意URL的有效性和安全性[^3]。 ```json { "button":[ { "type":"click", "name":"今日歌曲", "key":"V1001_TODAY_MUSIC" }, { "name":"菜单", "sub_button":[ { "type":"view", "name":"搜索", "url":"http://www.soso.com/" }, { "type":"miniprogram", "name":"wxa", "url":"http://mp.weixin.qq.com", "appid":"wx286b93c14bbf93aa", "pagepath":"/pages/lunar/index.html" } ] } ] } ``` #### 发送HTTP POST 请求至微信接口 使用上述准备好的JSON对象作为POST请求体的一部分来创建菜单。这里给出Java语言下的实现例子: ```java // 使用 HttpClient 或者 OkHttp 库发起 HTTP 请求 OkHttpClient client = new OkHttpClient(); RequestBody body = RequestBody.create(JSON, jsonMenu); Request request = new Request.Builder() .url("https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+accessToken) .post(body) .build(); Response response = client.newCall(request).execute(); if (!response.isSuccessful()) throw new IOException("Unexpected code " + response); System.out.println(response.body().string()); ``` 以上过程展示了如何通过编程的方式向微信服务器提交自定义菜单设置,并完成相应操作。需要注意的是,在实际应用中应当妥善保管应用程序ID(`appid`) 和密钥 (`appsecret`) ,并遵循安全编码实践以保护敏感信息的安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值