微信分享


文档:​qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html">​https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html​

1、微信后台配置

设置与开发/
公众号设置 / 功能设置 / JS接口安全域名 (配置网页所用的域名, 【需通过备案】)
基本配置 / IP白名单 (将服务器公网ip配置到此处)

2、后端代码

PHP版 参照官网给出的示例,稍作改造

PHP版本 7.1

目录结构:

./
JSSDK.php 微信分享必要参数获取基本接口
JSSDKCache.php 通过继承,实现对access_token、jsapi_ticket数据的缓存
RedisService.php Redis缓存服务
WeixinService.php 配置具体的微信参数,调用JSSDKCache.php
WeixinController.php 对外提供数据接口

用到的第三方库

# 网络请求
composer require rmccue/requests

# Redis客户端
composer require predis/predis

JSSDK.php

<?php
namespace app\utils\weixin;

use Requests;


/**
* 微信分享必要参数获取基本接口
*
* Class JSSDK
* @package app\utils\weixin
*/
class JSSDK
{
// 开发者ID
protected $appId;

// 开发者密码(AppSecret)
protected $appSecret;

public function __construct($appId, $appSecret)
{
$this->appId = $appId;
$this->appSecret = $appSecret;
}

public function getSignPackage($jsapi_ticket, $url)
{
$timestamp = time();
$nonceStr = $this->createNonceStr();

// 这里参数的顺序要按照 key 值 ASCII 码升序排序
$string = "jsapi_ticket=$jsapi_ticket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";

$signature = sha1($string);

$signPackage = array(
"appId" => $this->appId,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string
);

return $signPackage;
}

private function createNonceStr($length = 16)
{
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$str = "";
for ($i = 0; $i < $length; $i++) {
$str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $str;
}

/**
* 获取 Access token
* https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
*/
public function getAccessToken()
{
$params = [
'grant_type' => 'client_credential',
'appid' => $this->appId,
'secret' => $this->appSecret
];

$url = "https://api.weixin.qq.com/cgi-bin/token?" . http_build_query($params);

$data = self::httpGet($url);

if ($data) {
return empty($data['access_token']) ? null : $data['access_token'];
} else {
return null;
}
}

/**
* 获取 jsapi_ticket
* https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62
*/
public function getJsapiTicket($access_token)
{
$params = [
'access_token' => $access_token,
'type' => 'jsapi',
];

$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?" . http_build_query($params);

$data = self::httpGet($url);

if ($data) {
return empty($data['ticket']) ? null : $data['ticket'];
} else {
return null;
}
}

private function httpGet($url)
{
$response = Requests::get($url);

if ($response->success) {
return json_decode($response->body, true);
} else {
return null;
}
}
}

JSSDKCache.php

<?php

namespace app\utils\weixin;


use app\service\RedisService;


/**
* 自定义实现缓存: access_token、jsapi_ticket
*
* Class JSSDKCache
* @package app\utils\weixin
*/
class JSSDKCache extends JSSDK
{
public function getSignData($url)
{
$jsapi_ticket = $this->getJsapiTicketFromCache();

$data = $this->getSignPackage($jsapi_ticket, $url);

return $data;
}


public function getAccessTokenFromCache()
{
$cache_key = 'access_token.' . $this->appId;

$access_token = RedisService::get($cache_key);

if (!$access_token) {
// 获取access_token
$access_token = $this->getAccessToken();

// 7200
RedisService::set($cache_key, $access_token, 7100);
}

return $access_token;

}

public function getJsapiTicketFromCache()
{
$cache_key = 'jsapi_ticket.' . $this->appId;

$jsapi_ticket = RedisService::get($cache_key);

if (!$jsapi_ticket) {

// 获取access_token
$access_token = $this->getAccessTokenFromCache();

$jsapi_ticket = parent::getJsapiTicket($access_token);

// 7200
RedisService::set($cache_key, $jsapi_ticket, 7100);

}

return $jsapi_ticket;
}
}
<?php


namespace app\service;

use Predis\Client;

/**
* 使用Redis 缓存
*
* Class RedisService
* @package app\service
*/
class RedisService
{
// 可能有多个网站,每个网站给一个前缀
private static $prefix = 'www';

public static function getClient()
{
$client = new Client();
return $client;
}

public static function getKey($key)
{
return self::$prefix . '.' . $key;
}


public static function get($key)
{
$client = self::getClient();
$key = self::getKey($key);
return $client->get($key);
}

public static function set($key, $value, $expire = null)
{
//https://learnku.com/laravel/t/3563/using-predis-to-operate-the-redis-database-how-to-set-the-key-life-cycle-expires
$client = self::getClient();
$key = self::getKey($key);

if ($expire) {
// SET key value [EX seconds] [PX milliseconds] [NX|XX]
$client->set($key, $value, 'EX', $expire);
} else {
$client->set($key, $value);
}
}

}

WeixinService.php

<?php

namespace app\service;

use app\utils\weixin\JSSDKCache;


class WeixinService
{
// 公众号 开发者ID
private static $APP_ID = '';

// 开发者密码(AppSecret)
private static $APP_SECRET = '';


public static function getWeixinConfig($url)
{
$jssdk = new JSSDKCache(self::$APP_ID, self::$APP_SECRET);

return $jssdk->getSignData($url);
}
}

WeixinController.php

<?php

namespace app\api\controller;

use app\BaseController;

use app\service\WeixinService;

class WeixinController extends BaseController
{

public function getWeixinConfig()
{
$url = input('url');
return WeixinService::getWeixinConfig($url);
}
}

3、前端代码weixin-share.html

方式一:后端渲染

<!--微信分享-->


<script>
wx.config({
debug: false, // 开启调试模式
appId: '{$appId}', // 必填,公众号的唯一标识
timestamp: '{$timestamp}', // 必填,生成签名的时间戳
nonceStr: '{$nonceStr}', // 必填,生成签名的随机串
signature: '{$signature}',// 必填,签名
// 必填,需要使用的JS接口列表
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'],
// 可选,获取开放标签权限
openTagList: ['wx-open-launch-app']
});

// 自定义“分享给朋友”及“分享到QQ”按钮的分享内容
wx.ready(function () { //需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({
title: '{$title}', // 分享标题
desc: '{$summary}', // 分享描述
link: '{$url}', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "{$cover_icon}", // 分享图标
success: function () {
// 设置成功
}
});

// 分享到朋友圈
wx.updateTimelineShareData({
title: '{$title}', // 分享标题
link: '{$url}', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: "{$cover_icon}", // 分享图标
success: function () {
// 设置成功
}
})
});

wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
console.log("config验证失败")
});
</script>

方式二:前端通过接口获取

<!-- Axios -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<!--微信分享-->
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>


<script>
url = window.location.href.split('#')[0];
axios.post('/api/getWeixinConfig', {url: url}).then(res=>{
let data = res.data.data;

wx.config({
debug: false, // 开启调试模式
appId: data.appId, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature,// 必填,签名
// 必填,需要使用的JS接口列表
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'],
// 可选,获取开放标签权限
// openTagList: ['wx-open-launch-app']
});

// 自定义“分享给朋友”及“分享到QQ”按钮的分享内容
wx.ready(function () { //需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({
title: '{{ detail.title }}', // 分享标题
desc: '{{ detail.summary }}', // 分享描述
link: data.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '{{ detail.head_image }}', // 分享图标
success: function () {
// 设置成功
}
});

// 分享到朋友圈
wx.updateTimelineShareData({
title: '{{ detail.title }}', // 分享标题
link: data.url, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '{{ detail.head_image }}', // 分享图标
success: function () {
// 设置成功
}
})
});

wx.error(function(res){
// config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
console.log("config验证失败")
});
})


</script>