微信分享
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×tamp=$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>