微信公眾號網頁自定義分享(標題/描述/圖片/鏈接)是運營剛需。本文完整講解 JSSDK 簽名算法、access_token/jsapi_ticket 緩存策略、前端 wx.config 注入與分享接口調用,附生產級 PHP 封裝類與前端代碼。
access_token(7200s 有效,全局緩存)access_token 換取 jsapi_ticket(7200s 有效,全局緩存)jsapi_ticket=...&noncestr=...×tamp=...&url=... → SHA1 簽名appId, timestamp, nonceStr, signature 傳給前端模板jweixin-1.0.0.js → wx.config({...}) 驗證wx.ready 中調用 onMenuShareTimeline/AppMessage/QQ/Weibo 設置分享內容class WechatJSSDK {
private $appId, $appSecret;
private $cache; // Redis/Memcached 實例
public function __construct($appId, $appSecret, $cache) {
$this->appId = $appId;
$this->appSecret = $appSecret;
$this->cache = $cache;
}
// 獲取 access_token(緩存 7000s)
private function getAccessToken() {
$key = 'wx_access_token_' . $this->appId;
if ($token = $this->cache->get($key)) return $token;
$url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}";
$res = json_decode(httpGet($url), true);
if (isset($res['access_token'])) {
$this->cache->set($key, $res['access_token'], 7000);
return $res['access_token'];
}
throw new Exception('AccessToken獲取失敗: ' . ($res['errmsg'] ?? '未知'));
}
// 獲取 jsapi_ticket(緩存 7000s)
private function getJsapiTicket() {
$key = 'wx_jsapi_ticket_' . $this->appId;
if ($ticket = $this->cache->get($key)) return $ticket;
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" . $this->getAccessToken() . "&type=jsapi";
$res = json_decode(httpGet($url), true);
if (isset($res['ticket'])) {
$this->cache->set($key, $res['ticket'], 7000);
return $res['ticket'];
}
throw new Exception('JsapiTicket獲取失敗: ' . ($res['errmsg'] ?? '未知'));
}
// 核心:生成簽名配置陣列
public function getSignPackage($url = null) {
$url = $url ?? 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// 去除 # 及後面部分
if (($pos = strpos($url, '#')) !== false) $url = substr($url, 0, $pos);
$jsapiTicket = $this->getJsapiTicket();
$nonceStr = $this->randomStr(16);
$timestamp = time();
$string = "jsapi_ticket={$jsapiTicket}&noncestr={$nonceStr}×tamp={$timestamp}&url={$url}";
$signature = sha1($string);
return [
'appId' => $this->appId,
'nonceStr' => $nonceStr,
'timestamp' => $timestamp,
'signature' => $signature,
'rawString' => $string, // 調試用
];
}
private function randomStr($len = 16) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$str = '';
for ($i = 0; $i < $len; $i++) $str .= $chars[mt_rand(0, strlen($chars) - 1)];
return $str;
}
}
$jssdk = new WechatJSSDK('APPID', 'SECRET', $redis);
$signPackage = $jssdk->getSignPackage();
// 分配給模板引擎
$view->assign('signPackage', $signPackage);
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<script>
wx.config({
debug: false, // 生產關閉
appId: '<?= $signPackage['appId'] ?>',
timestamp: '<?= $signPackage['timestamp'] ?>',
nonceStr: '<?= $signPackage['nonceStr'] ?>',
signature: '<?= $signPackage['signature'] ?>',
jsApiList: [
'onMenuShareTimeline', 'onMenuShareAppMessage',
'onMenuShareQQ', 'onMenuShareWeibo',
'updateAppMessageShareData', 'updateTimelineShareData' // 1.4.0+ 新接口
]
});
wx.ready(function() {
const shareData = {
title: '自定義分享標題',
desc: '自定義分享描述',
link: location.href.split('#')[0], // 必須與簽名 URL 一致
imgUrl: 'https://your.com/share.jpg',
success: () => console.log('分享成功'),
cancel: () => console.log('用戶取消')
};
// 兼容舊版接口(逐漸棄用)
wx.onMenuShareTimeline(shareData);
wx.onMenuShareAppMessage(shareData);
wx.onMenuShareQQ(shareData);
wx.onMenuShareWeibo(shareData);
// 新版接口(推薦)
wx.updateAppMessageShareData(shareData);
wx.updateTimelineShareData(shareData);
});
wx.error(function(res) {
console.error('JSSDK驗證失敗:', res);
// 可上報日誌分析
});
</script>
| # | 坑點 | 解決方案 |
|---|---|---|
| 1 | 簽名 URL 與當前頁 URL 不一致 | 簽名用 location.href.split('#')[0],前端分享 link 必須完全一致 |
| 2 | access_token / jsapi_ticket 未緩存 | 必須全局緩存 7000s,否則高併發觸發微信頻控封禁 |
| 3 | nonceStr/timestamp 前後端不一致 | 簽名用的參數必須原樣傳給 wx.config |
| 4 | 未配置 JS 接口安全域名 | 公眾平台 → 設置 → 功能設置 → JS接口安全域名(無需協議頭) |
| 5 | 測試環境域名不在安全域名 | 用內網穿透映射到已備案域名,或配置測試號 |
| 6 | 分享圖片無法加載 | 圖片必須 HTTPS、<300KB、建議 300x300px、域名需在安全域名下 |
| 7 | iOS 微信分享到朋友圈無圖/無描述 | 必須設置 updateTimelineShareData,舊接口 onMenuShareTimeline 不支援 desc |
| 8 | 頁面動態路由導致 URL 變化 | SPA 頁面路由切換需重新獲取簽名並 wx.config |
| 9 | 企業微信/服務號/訂閱號權限差異 | 認證服務號/企業微信才有全部分享接口,訂閱號受限 |
| 10 | 簽名算法大小寫/排序錯誤 | 參數按 ASCII 碼升序拼接,key 小寫,值不編碼,SHA1 大寫輸出 |
debug: true,控制台會輸出具體錯誤碼rawString 與前端 wx.config 參數是否完全一致將上述邏輯封裝為 Composer 包或框架組件,即可在任何項目中一行代碼接入微信分享,徹底告別「分享出去是默認標題」的尷尬。