一、简介
微信公众号开发中,微信JS-SDK是实现自定义分享功能的核心技术。在使用微信分享、拍照、录音、获取地理位置等功能之前,必须通过权限验证配置(签名算法)来确保安全。
本文以完整的PHP实现为例,详细介绍如何获取微信JSSDK所需要的jsapi_ticket、生成权限签名,以及对常见问题的处理方案。
二、JS-SDK使用权限签名算法概述
签名生成规则如下:参与签名的字段包括noncestr(随机字符串)、jsapi_ticket(有效的票据)、timestamp(时间戳)、url(当前网页的URL,不包含#及其后面部分)。
签名步骤:
- 对所有待签名参数按照字段名的ASCII码从小到大排序(字典序)
- 使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1
- 所有参数名均为小写字符
- 对string1作sha1加密,字段名和字段值都采用原始值,不进行URL转义
三、完整PHP实现
3.1 微信JSSDK类(Wxsdk)
<?php
/**
* 微信JSSDK分享接口实现类
* 用于获取签名包,供前端wx.config使用
*/
class Wxsdk
{
private $appId;
private $appSecret;
/**
* 构造函数
* @param string $appId 公众号AppID
* @param string $appSecret 公众号AppSecret
*/
public function __construct($appId, $appSecret)
{
$this->appId = $appId;
$this->appSecret = $appSecret;
}
/**
* 获取JSSDK签名包
* @param string $url 当前页面URL(可选,不传则自动获取)
* @return array 签名信息数组
*/
public function getSignPackage($url = '')
{
// 获取jsapi_ticket
$jsapiTicket = $this->getJsApiTicket();
// 生成时间戳
$timestamp = time();
// 生成随机字符串
$nonceStr = $this->createNonceStr();
// 获取当前页面URL
if (empty($url)) {
$url = $this->getCurrentUrl();
} else {
// 解码前端传递过来的URL
$url = urldecode($url);
}
// 这里参数的顺序要按照key值ASCII码升序排序
$string = "jsapi_ticket=$jsapiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
$signature = sha1($string);
return [
"appId" => $this->appId,
"nonceStr" => $nonceStr,
"timestamp" => $timestamp,
"url" => $url,
"signature" => $signature,
"rawString" => $string
];
}
/**
* 生成随机字符串
* @param int $length 字符串长度
* @return string
*/
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;
}
/**
* 获取jsapi_ticket
* 有效期7200秒,需要全局缓存
* @return string
*/
private function getJsApiTicket()
{
// jsapi_ticket 应该全局存储与更新,以下代码以写入到文件中做示例
$data = json_decode(file_get_contents("jssdk/jsapi_ticket.json"));
if ($data->expire_time < time xss=removed>getAccessToken();
$url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" . $accessToken . "&type=jsapi";
$res = json_decode($this->httpGet($url));
$ticket = isset($res->ticket) ? $res->ticket : null;
if ($ticket) {
$data->expire_time = time() + 7000;
$data->jsapi_ticket = $ticket;
file_put_contents("jssdk/jsapi_ticket.json", json_encode($data));
}
} else {
$ticket = $data->jsapi_ticket;
}
return $ticket;
}
/**
* 获取access_token
* 有效期7200秒,需要全局缓存
* @return string
*/
private function getAccessToken()
{
// access_token 应该全局存储与更新,以下代码以写入到文件中做示例
$data = json_decode(file_get_contents("jssdk/access_token.json"));
if ($data->expire_time < time url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=">appId . "&secret=" . $this->appSecret;
$res = json_decode($this->httpGet($url));
$access_token = isset($res->access_token) ? $res->access_token : null;
if ($access_token) {
$data->expire_time = time() + 7000;
$data->access_token = $access_token;
file_put_contents("jssdk/access_token.json", json_encode($data));
}
} else {
$access_token = $data->access_token;
}
return $access_token;
}
/**
* 获取当前页面完整URL
* @return string
*/
private function getCurrentUrl()
{
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || $_SERVER['SERVER_PORT'] == 443) ? "https://" : "http://";
return "$protocol$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
}
/**
* HTTP GET请求
* @param string $url 请求地址
* @return string
*/
private function httpGet($url)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$output = curl_exec($ch);
if (curl_errno($ch)) {
die('Curl error: ' . curl_error($ch));
}
curl_close($ch);
return $output;
}
}
3.2 初始化JSON缓存文件
在jssdk目录下创建两个JSON缓存文件:
access_token.json
{
"access_token": "",
"expire_time": 0
}
jsapi_ticket.json
{
"jsapi_ticket": "",
"expire_time": 0
}
四、前后端整合使用
4.1 后端接口(签名接口)
<?php
// sign.php - 前端通过AJAX请求获取签名
require_once 'Wxsdk.php';
// 配置你的AppID和AppSecret
$appId = '你的AppID';
$appSecret = '你的AppSecret';
$wxsdk = new Wxsdk($appId, $appSecret);
// 获取前端传递过来的当前页面URL
$url = isset($_POST['url']) ? $_POST['url'] : '';
// 获取签名包
$signPackage = $wxsdk->getSignPackage($url);
// 返回JSON
header('Content-Type: application/json');
echo json_encode([
'code' => 1,
'msg' => 'success',
'data' => $signPackage
]);
4.2 前端JavaScript代码
<!-- 引入微信JSD -->
[removed][removed]
[removed]
// 页面加载时获取签名配置
jQuery.post("/sign.php", {
url: encodeURIComponent[removed].href.split('#')[0]),
t: new Date().getTime()
}, function(result) {
if (result.code != 1) {
console.error('获取签名失败');
return;
}
var sign = result.data;
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来
appId: sign.appId, // 必填,公众号的唯一标识
timestamp: sign.timestamp, // 必填,生成签名的时间戳
nonceStr: sign.nonceStr, // 必填,生成签名的随机串
signature: sign.signature, // 必填,签名
jsApiList: [
'updateAppMessageShareData', // 分享给朋友(新接口)
'updateTimelineShareData', // 分享到朋友圈(新接口)
'onMenuShareAppMessage', // 分享给朋友(旧接口,兼容)
'onMenuShareTimeline', // 分享到朋友圈(旧接口,兼容)
'onMenuShareQQ', // 分享到QQ
'onMenuShareWeibo', // 分享到微博
'onMenuShareQZone' // 分享到QQ空间
]
});
// 配置成功后执行
wx.ready(function() {
console.log('JSSDK配置成功');
// 自定义分享给朋友
wx.updateAppMessageShareData({
title: '自定义分享标题', // 分享标题
desc: '自定义分享描述', // 分享描述
link: [removed].href, // 分享链接
imgUrl: 'https://example.com/share-icon.png', // 分享图标
success: function() {
console.log('分享设置成功');
}
});
// 自定义分享到朋友圈
wx.updateTimelineShareData({
title: '朋友圈分享标题', // 分享标题
link: [removed].href, // 分享链接
imgUrl: 'https://example.com/share-icon.png', // 分享图标
success: function() {
console.log('朋友圈设置成功');
}
});
});
// 配置失败
wx.error(function(res) {
console.log('JSSDK配置失败:' + JSON.stringify(res));
});
}, 'json');
五、重要注意事项
5.1 缓存机制(必须实现)
access_token和jsapi_ticket有效期均为7200秒,微信接口调用次数有限制,频繁获取会被限流!
推荐缓存方案:
- 文件缓存:如上文示例,将token写入JSON文件
- Redis缓存:适合高并发场景,设置过期时间为7000秒
- 数据库缓存:适合分布式系统
5.2 JS接口安全域名设置
在调用JSSDK之前,必须先在微信公众平台完成以下配置:
- 登录微信公众平台:https://mp.weixin.qq.com
- 进入"设置与开发" → "基本配置" → "JS接口安全域名"
- 设置要使用的域名(不带http://,不带路径)
- 点击"添加"保存
5.3 URL签名注意事项
- 签名用的url必须是调用JS接口页面的完整URL
- URL不包含#及其后面部分,需要在JavaScript中通过
location.href.split('#')[0]获取 - 如果是动态页面,需要用PHP动态获取当前URL
- 页面一旦分享,微信客户端会在链接末尾加入其他参数,必须动态获取当前链接
- 前端传递URL时需要
encodeURIComponent编码,后端用urldecode解码
5.4 IP白名单配置
如果服务器有IP白名单限制,需要将服务器IP添加到微信公众平台的IP白名单中:
- 进入"基本配置" → "IP白名单"
- 添加服务器公网IP地址
六、常见问题及解决
6.1 错误:config:invalid signature
原因:签名生成错误
排查步骤:
- 确认jsapi_ticket是否有效且未过期
- 确认url是否包含#及后面的内容
- 确认nonceStr与wx.config中的nonceStr完全一致
- 确认生成签名的url与当前页面url一致
- 确认nonceStr和timestamp是大小写敏感
- 使用开发者工具或打日志输出签名信息,与官方签名校验工具对比
6.2 错误:config:url not in whitelist
原因:调用JSSDK的页面域名未在JS接口安全域名中配置
解决:在微信公众平台的"JS接口安全域名"中添加对应域名
6.3 无法调用微信JS接口
可能原因:
- 引入了错误的JSSDK文件(建议使用官方CDN)
- jsApiList中未包含要调用的接口名称
- 未通过wx.ready回调调用接口
- AppID和AppSecret配置错误
6.4 分享链接签名失败
原因:用户分享后微信会在URL末尾追加参数
解决:对于Vue/SPA等单页应用,必须在每次页面切换时重新签名,或通过前端动态获取URL传到后端进行签名
// Vue项目中动态获取URL进行签名的方式
axios.post('/jssdk/sign', { url: location.href.split('#')[0] })
.then(function(res) {
wx.config({
appId: res.data.appId,
timestamp: res.data.timestamp,
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData']
});
});
6.5 签名校验工具
可以使用微信官方签名校验工具验证签名是否正确:
http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
七、完整流程图
JSSDK签名的完整流程如下:
- 前端 → 访问微信页面,获取当前URL并编码
- 前端 → 通过AJAX将URL传递给后端签名接口
- 后端 → 获取access_token(使用AppID和AppSecret调用微信接口)
- 后端 → 使用access_token获取jsapi_ticket
- 后端 → 使用jsapi_ticket、随机字符串、时间戳、URL生成签名
- 后端 → 将签名包(appId、nonceStr、timestamp、signature、url)返回前端
- 前端 → 使用wx.config注入配置信息
- 前端 → 在wx.ready中调用微信JSSDK接口
八、总结
微信JSSDK页面授权(签名算法)是微信公众号H5开发的核心环节,需要注意以下关键点:
- access_token和jsapi_ticket必须缓存,避免频繁调用接口被封禁
- 签名URL必须与调用页面的完整URL完全一致
- 必须在微信公众平台设置JS接口安全域名
- 所有签名参数和wx.config中的参数必须完全一致
- 在wx.ready回调中调用JSSDK接口,不要在其他位置调用
- 前端传递URL时需要encodeURIComponent编码,后端需要urldecode解码

发表评论 取消回复