微信JS-SDK签名需先获取并缓存access_token与jsapi_ticket,再用nonceStr、timestamp、jsapi_ticket和当前完整URL按字典序拼接后SHA1加密;应封装为可复用服务类,避免控制器硬编码。

微信JS-SDK签名不是简单拼接参数就能搞定的,核心在于正确生成jsapi_ticket和nonceStr、timestamp,再按规则拼接签名原文并用SHA256哈希。PHP框架里做这件事,关键是要把签名逻辑解耦、可复用,并适配框架的生命周期(比如自动刷新过期的jsapi_ticket)。
获取access_token与jsapi_ticket
微信JS-SDK签名依赖jsapi_ticket,而它又依赖全局唯一的access_token。两者有效期均为2小时,必须缓存并自动刷新。
- 建议使用框架自带的缓存组件(如Laravel的Cache、ThinkPHP的cache()、CodeIgniter的redis驱动)存储
access_token和jsapi_ticket,键名可设为wechat_access_token和wechat_jsapi_ticket - 每次调用前先查缓存,失效则调用微信接口刷新(
https://api.weixin.qq.com/cgi-bin/token和https://api.weixin.qq.com/cgi-bin/ticket/getticket) - 注意:获取
jsapi_ticket时type必须为jsapi,别错写成card或漏传
构造签名所需参数
前端调用wx.config()前,后端需返回appId、timestamp、nonceStr、signature四个字段。其中signature是核心。
-
nonceStr建议用bin2hex(random_bytes(16))生成32位随机字符串(PHP 7+),避免time()+rand()这种易碰撞方式 -
timestamp直接用time(),不要格式化,必须是整型秒级时间戳 - 签名原文按字典序拼接:
jsapi_ticket=xxx&noncestr=yyy×tamp=zzz&url=https://yourdomain.com/path(注意url必须是当前页面完整URL,含协议、域名、路径、查询参数,不含#及后面片段)
封装签名服务类(以Laravel为例)
不推荐在控制器里硬编码签名逻辑,应抽成服务类,便于测试和复用。
class WechatJSSDK
{
protected $appId;
protected $appSecret;
<pre class="brush:php;toolbar:false;">public function __construct($appId, $appSecret)
{
$this->appId = $appId;
$this->appSecret = $appSecret;
}
public function getSignature($url)
{
$jsapiTicket = $this->getJsApiTicket();
$nonceStr = bin2hex(random_bytes(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,
];
}
protected function getJsApiTicket()
{
$ticket = Cache::get('wechat_jsapi_ticket');
if ($ticket && isset($ticket['ticket']) && $ticket['expires_in'] > time()) {
return $ticket['ticket'];
}
$accessToken = $this->getAccessToken();
$response = file_get_contents("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$accessToken}&type=jsapi");
$data = json_decode($response, true);
if (empty($data['ticket'])) {
throw new \Exception('Failed to fetch jsapi_ticket: ' . $data['errmsg']);
}
Cache::put('wechat_jsapi_ticket', [
'ticket' => $data['ticket'],
'expires_in' => time() + $data['expires_in'] - 300, // 提前5分钟过期
], $data['expires_in'] / 60);
return $data['ticket'];
}
protected function getAccessToken()
{
$token = Cache::get('wechat_access_token');
if ($token && $token['expires_in'] > time()) {
return $token['access_token'];
}
$response = file_get_contents("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}");
$data = json_decode($response, true);
if (empty($data['access_token'])) {
throw new \Exception('Failed to fetch access_token: ' . $data['errmsg']);
}
Cache::put('wechat_access_token', [
'access_token' => $data['access_token'],
'expires_in' => time() + $data['expires_in'] - 300,
], $data['expires_in'] / 60);
return $data['access_token'];
}}
使用 JSON Schema 验证 JSON 数据,从示例 JSON 生成 schema,并将其转换为 TypeScript 接口、Python 数据类或 Markdown 文档。
在控制器中调用示例
前端访问页面时,通过API返回签名配置,供wx.config()使用。
return response()->json($sdk->getSignature($url));
前端拿到后直接传入:
wx.config({
appId: data.appId,
timestamp: data.timestamp,
nonceStr: data.nonceStr,
signature: data.signature,
jsApiList: ['chooseImage', 'uploadImage']
});
不复杂但容易忽略:签名用的url必须和服务端实际渲染该页面的URL完全一致,尤其是带参数的页面,务必用$request->fullUrl()而非$_SERVER['HTTP_REFERER']或硬编码路径。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










