From ebb617bb967ab9987d2acdd795f204743389d4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E6=B6=9B?= Date: Wed, 13 Aug 2025 23:43:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BA=86=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E7=AB=AF=E5=AE=9E=E7=8E=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.MD | 208 +++++++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 203 insertions(+), 7 deletions(-) diff --git a/README.MD b/README.MD index 17a4dfa..369ca67 100644 --- a/README.MD +++ b/README.MD @@ -98,6 +98,14 @@ wxSDK({ ### `callback.error(error)` 当发生错误时触发,参数为错误对象 +## 注意事项 + +1. **跨域问题**:确保后端签名接口支持 CORS 或使用代理 +2. **URL 一致性**:签名 URL 必须与当前页面 URL 完全一致(不含 hash) +3. **HTTPS**:微信要求所有页面必须使用 HTTPS +4. **开放标签**:使用开放标签需在微信公众平台申请 +5. **图标大小**:微信分享图标建议尺寸 200×200 像素 + ## 后端接口要求 SDK 需要调用后端接口获取微信签名,接口应返回以下格式的 JSON 数据: @@ -111,10 +119,198 @@ SDK 需要调用后端接口获取微信签名,接口应返回以下格式的 } ``` -## 注意事项 +## 后端 Express 实现 -1. **跨域问题**:确保后端签名接口支持 CORS 或使用代理 -2. **URL 一致性**:签名 URL 必须与当前页面 URL 完全一致(不含 hash) -3. **HTTPS**:微信要求所有页面必须使用 HTTPS -4. **开放标签**:使用开放标签需在微信公众平台申请 -5. **图标大小**:微信分享图标建议尺寸 200×200 像素 \ No newline at end of file +### 环境要求 + +1. Node.js 14+ +2. Express 4.x +3. Axios 1.x + +### 安装依赖 + +```bash +npm install express axios +``` + +### 具体实现 + +```javascript +const express = require('express'); +const axios = require('axios'); +const crypto = require('crypto'); +const router = express.Router(); + +// 缓存 access_token 和 ticket +let cacheToken = null; +let cacheTicket = null; +let tokenExpireTime = 0; +let ticketExpireTime = 0; + +// 微信分享签名接口 +router.get('/wxShare', async (req, res) => { + // 微信公众号配置 + const appId = 'appId'; + const appsecret = 'appsecret'; + + // 获取前端传递的当前页面 URL + const { url } = req.query; + + // 验证参数 + if (!url) { + return res.status(400).json({ + code: 400, + message: 'URL参数不能为空' + }); + } + + try { + // 检查并刷新 access_token + const now = Math.floor(Date.now() / 1000); + if (!cacheToken || now >= tokenExpireTime) { + const tokenResponse = await axios.get( + `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${appsecret}` + ); + + if (tokenResponse.data.errcode) { + throw new Error(`获取access_token失败: ${tokenResponse.data.errmsg}`); + } + + cacheToken = tokenResponse.data.access_token; + // 提前10分钟过期 + tokenExpireTime = now + tokenResponse.data.expires_in - 600; + } + + // 检查并刷新 ticket + if (!cacheTicket || now >= ticketExpireTime) { + const ticketResponse = await axios.get( + `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${cacheToken}&type=jsapi` + ); + + if (ticketResponse.data.errcode !== 0) { + throw new Error(`获取ticket失败: ${ticketResponse.data.errmsg}`); + } + + cacheTicket = ticketResponse.data.ticket; + // 提前10分钟过期 + ticketExpireTime = now + ticketResponse.data.expires_in - 600; + } + + // 生成签名 + const nonceStr = generateNonceStr(16); + const timestamp = Math.floor(Date.now() / 1000); + + const signature = generateSignature({ + jsapi_ticket: cacheTicket, + noncestr: nonceStr, + timestamp, + url + }); + + res.json({ + appId, + nonceStr, + timestamp, + signature + }); + + } catch (err) { + console.error('微信分享签名错误:', err); + + // 重置缓存 + cacheToken = null; + cacheTicket = null; + + res.status(500).json({ + code: 500, + message: '服务器内部错误', + error: err.message + }); + } +}); + +// 生成随机字符串 +function generateNonceStr(length = 16) { + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let nonceStr = ''; + + for (let i = 0; i < length; i++) { + nonceStr += chars.charAt(Math.floor(Math.random() * chars.length)); + } + + return nonceStr; +} + +// 生成签名 +function generateSignature(params) { + const string = Object.keys(params) + .sort() + .map(key => `${key}=${params[key]}`) + .join('&'); + + return crypto.createHash('sha1').update(string).digest('hex'); +} + +module.exports = router; +``` + +### 错误响应 + +```json +{ + "code": 400, + "message": "URL参数不能为空" +} +``` + +```json +{ + "code": 500, + "message": "服务器内部错误", + "error": "获取access_token失败: invalid appid" +} +``` + +### 启动服务 + +在 `app.js` 中集成路由: + +```javascript +const express = require('express'); +const wxShareRouter = require('./routes/wxShare'); + +const app = express(); + +// 中间件 +app.use(express.json()); +app.use(express.urlencoded({ extended: true })); + +// 路由 +app.use('/api', wxShareRouter); + +// 错误处理 +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).send('服务器错误'); +}); + +// 启动服务器 +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => { + console.log(`服务器运行在端口 ${PORT}`); +}); +``` + +## 常见问题 + +### 1. 签名无效 (invalid signature) + +可能原因: +- URL 不一致(前端传入的 URL 必须与当前页面完全一致) +- 时间戳不一致(确保服务器时间准确) +- 签名算法错误(严格按照微信文档实现) + +解决方案: +- 验证前端传入的 URL 是否去除 hash 部分 +- 检查服务器时间是否同步 +- 使用微信官方签名校验工具验证 \ No newline at end of file diff --git a/package.json b/package.json index 2f2ba2c..92a150b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wxsdk-pure", - "version": "1.0.2", + "version": "1.0.3", "description": "微信分享 SDK 封装", "main": "index.js", "scripts": {