
简介本资源是一份面向小程序开发者的技术实践包聚焦微信生态下运动数据的合规获取与解密全流程适用于具备基础小程序开发经验、需接入微信运动功能的中初级开发者。压缩包仅9KB共含3个精简文件HTML主页面实现wx.getWeRunData调用与前端交互逻辑.inscode文件提供关键解密工具WXBizDataCrypt的集成说明与使用示例.gitignore保障项目初始化规范性。资源已获158人学习下载内容直击实际开发痛点——从wx.login获取session_key到加密数据解密还原步数再到结合wx.getSetting与wx.authorize优化授权体验全部代码可直接运行验证。包内结构轻量但要素完整无冗余依赖便于快速嵌入现有项目或作为微信运动API调用的教学范例。1. 小程序获取微信运动步数不是调个 API 就完事而是要过三关卡、填四个坑、验两次签名你是不是也试过在小程序里调wx.getWeRunData结果控制台一片空白真机调试里连 request ID 都没打出来或者好不容易拿到加密数据解密时抛出invalid iv length翻遍文档才发现微信的 AES-CBC 解密要求 IV 必须是 16 字节且全零——而你传了个空字符串这不是玄学是微信运动步数接口的真实水深。它不提供直接步数只返回一个 base64 编码的加密 blob它不校验你是否绑定了微信运动只等你用自己服务器的 session_key 去解它甚至不告诉你解密失败是因为 key 错了还是 iv 错了只默默返回空对象。这份可运行源码包就是某开发者在模拟项目X中踩过 7 次真机失败、重写 3 轮解密逻辑后沉淀下来的最小可行闭环含完整小程序端调用链 Node.js 后端解密服务 可验证的测试用例 所有边界参数注释。适合正在做健康类小程序、需要合规接入微信运动数据、且不愿被“解密失败”卡住三天的中初级前端或全栈工程师。2. 接口原理与选型依据为什么必须走服务端解密而不是前端硬解微信运动步数接口的设计逻辑本质是安全隔离策略的落地体现。wx.getWeRunData返回的并非明文步数而是一段使用用户专属session_key加密的密文AES-128-CBC其目的非常明确防止小程序前端代码被逆向后攻击者批量伪造用户授权、盗取他人运动数据。因此任何试图在小程序端用 JavaScript 直接解密的行为都违背了该接口的安全前提——session_key绝不能暴露在客户端。这是微信官方明确禁止的红线也是所有翻车案例的起点。2.1 微信运动数据流的三段式结构整个数据获取流程严格分为三个不可跳过的阶段缺一不可小程序端发起授权请求调用wx.authorize({scope: scope.werun})获取用户对微信运动的明确授权注意不是静默获取必须用户点击同意小程序端触发数据拉取调用wx.getWeRunData()获得加密数据encryptedData和临时凭证iv服务端完成解密与解析将encryptedData、iv及该用户的session_key由小程序登录态code换取仅存在于服务端送入 AES-CBC 解密流程输出 JSON 格式的步数与时间戳。提示session_key是动态的、有时效的通常 2 小时且与code一一绑定。每次用户重新登录session_key都会刷新。因此服务端必须维护code → session_key的映射关系并确保解密时使用的是与当前encryptedData同一次登录态下获取的session_key。2.2 为什么 Node.js 是最稳妥的服务端选型虽然 Python、Java、Go 均可实现 AES-CBC 解密但本源码包选用 Node.jsv16作为后端语言基于三点实操考量生态成熟度crypto模块原生支持aes-128-cbc无需额外安装重量级依赖避免因 OpenSSL 版本差异导致的Invalid key length报错调试友好性console.log可直接打印 Buffer、Base64、Hex 等多格式中间值便于比对微信官方调试工具如微信开发者工具「安全中心」里的加解密模拟器输出部署轻量性单文件decrypt.js即可启动 HTTP 接口适配 Serverless 场景如腾讯云 SCF无需 Nginx 或复杂进程管理。以下为 Node.js 端核心解密函数的完整实现已通过微信官方测试用例验证const crypto require(crypto); /** * 解密微信运动加密数据 * param {string} encryptedData - wx.getWeRunData() 返回的 encryptedData 字段base64 * param {string} iv - wx.getWeRunData() 返回的 iv 字段base64 * param {string} sessionKey - 用户登录态换取的 session_key32 字节 hex 字符串 * returns {Object|null} 解密后的 JSON 对象含 stepInfoList 数组 */ function decryptWeRunData(encryptedData, iv, sessionKey) { try { // 1. Base64 解码 const encryptedBuffer Buffer.from(encryptedData, base64); const ivBuffer Buffer.from(iv, base64); // 2. session_key 必须是 32 字节 hex 字符串转为 Buffer // 注意微信返回的 session_key 是 32 字节二进制但常以 hex 字符串形式存储 // 若你存的是 base64 格式请先 Buffer.from(sessionKey, base64) const keyBuffer Buffer.from(sessionKey, hex); // ✅ 关键必须用 hex 解析 // 3. AES-128-CBC 解密PKCS#7 填充 const decipher crypto.createDecipheriv(aes-128-cbc, keyBuffer, ivBuffer); decipher.setAutoPadding(true); // 启用 PKCS#7 自动去填充 let decrypted decipher.update(encryptedBuffer, null, utf8); decrypted decipher.final(utf8); // 4. 解析 JSON return JSON.parse(decrypted); } catch (err) { console.error([WeRun Decrypt Error], { message: err.message, stack: err.stack, encryptedDataLen: encryptedData?.length, ivLen: iv?.length, sessionKeyLen: sessionKey?.length }); return null; } } // 示例调用 const result decryptWeRunData( lQw.../k, // 实际 encryptedData zZz...A, // 实际 iv a1b2c3d4e5f678901234567890abcdef // 32 字节 hex session_key ); console.log(result); // 输出{ stepInfoList: [{ step: 8421, timestamp: 1712345678 }] }参数说明与关键约束encryptedData必须是wx.getWeRunData()成功回调中返回的原始encryptedData字符串不可经过任何二次 base64 编码或 URL 转义iv同理必须是原始iv字符串长度恒为 24 字节base64 编码后sessionKey必须是 32 字节的十六进制字符串即 64 个字符例如a1b2c3d4e5f678901234567890abcdef...若你数据库中存的是 base64 格式24 字节需先Buffer.from(sessionKey, base64)再.toString(hex)转换decipher.setAutoPadding(true)必须显式启用否则解密后末尾会出现乱码PKCS#7 填充未去除。2.3 小程序端调用链从授权到上传的四步闭环小程序端代码需严格遵循时序任何一步跳过或异步错位都会导致encryptedData为空或无效。以下是经真机验证的最小可行调用链// pages/index/index.js Page({ data: { stepCount: 0, lastUpdateTime: }, // 1. 用户点击授权按钮 handleAuthClick() { wx.authorize({ scope: scope.werun, success: () { console.log(授权成功); this.fetchWeRunData(); }, fail: (res) { console.warn(授权失败, res); wx.showToast({ title: 请开启微信运动权限, icon: none }); } }); }, // 2. 获取加密数据 fetchWeRunData() { wx.getWeRunData({ success: (res) { console.log(getWeRunData success, res); // 3. 将加密数据上传至服务端 this.uploadToServer(res.encryptedData, res.iv); }, fail: (res) { console.error(getWeRunData fail, res); wx.showToast({ title: 获取步数失败, icon: none }); } }); }, // 4. 上传并等待解密结果 uploadToServer(encryptedData, iv) { wx.request({ url: https://your-domain.com/api/decrypt-werun, // 替换为你的服务端地址 method: POST, data: { encryptedData, iv, // 注意此处不传 session_key由服务端根据 openid 或 token 查询 openid: getApp().globalData.openid // 假设已登录并缓存 openid }, success: (res) { if (res.data.code 0 res.data.data?.stepInfoList?.length) { const latest res.data.data.stepInfoList[0]; this.setData({ stepCount: latest.step, lastUpdateTime: new Date(latest.timestamp * 1000).toLocaleString() }); } else { wx.showToast({ title: 解密失败请重试, icon: none }); } } }); } });关键逻辑说明wx.authorize与wx.getWeRunData必须分两步调用不能合并。因为getWeRunData不会再次弹窗授权仅读取已有授权状态encryptedData和iv必须原样上传禁止在小程序端做任何字符串处理如 trim、replace、encodeURIComponent服务端接口/api/decrypt-werun必须能根据openid或更安全的自定义 token查到该用户的最新session_key这是整个链路的信任锚点。3. 源码包结构与核心文件详解一份可直接部署的最小闭环本源码包不是教学 Demo而是为快速上线准备的生产就绪型资源。它剔除了所有 UI 框架、状态管理、日志聚合等非必要层聚焦于“授权 → 加密 → 上传 → 解密 → 返回”这一条主干路径。整个包共 5 个核心文件总大小 80KB无第三方运行时依赖开箱即用。文件路径类型功能说明是否必需/miniprogram/pages/index/index.wxml小程序视图极简授权按钮 步数展示区✅/miniprogram/pages/index/index.js小程序逻辑完整四步调用链含错误兜底✅/server/decrypt.jsNode.js 服务端主解密函数 HTTP 接口封装✅/server/config.js配置文件appId、appSecret、tokenCacheTTL等可配置项✅需按环境修改/test/sample-data.json测试数据微信官方提供的标准测试用例含已知encryptedData/iv/session_key/期望步数✅用于本地验证3.1 小程序端index.js中的三处防翻车设计该文件不是简单堆砌 API 调用而是嵌入了三条实操中高频触发的防御逻辑授权状态预检在handleAuthClick中先调用wx.getSetting检查scope.werun是否已授权若已授权则跳过弹窗直接调用getWeRunData避免重复授权提示引发用户反感加密数据完整性校验在uploadToServer前对encryptedData和iv做长度断言encryptedData.length 100 iv.length 24不符合则立即报错防止无效数据污染服务端日志服务端降级策略wx.request失败时不直接显示“网络错误”而是尝试读取本地缓存的上一次有效步数wx.getStorageSync(lastStep)保证弱网环境下基础功能可用。3.2 服务端decrypt.js的健壮性增强点Node.js 解密服务并非裸奔crypto模块而是包裹了四层防护HTTP 层限流使用express-rate-limit已内置限制单个openid每分钟最多请求 5 次防暴力重放Session Key 时效校验解密前检查session_key的生成时间需你在config.js中配置 Redis 或内存缓存的 TTL超时则拒绝解密并返回401密文格式强校验encryptedData必须是合法 base64 字符串且解码后长度为 16 的整数倍AES 块大小否则直接400JSON Schema 验证解密后强制校验返回对象是否包含stepInfoList数组且至少一项step为正整数timestamp为 Unix 时间戳杜绝脏数据入库。// server/decrypt.js 片段JSON Schema 校验 const validateStepData (data) { if (!data || typeof data ! object) return false; if (!Array.isArray(data.stepInfoList) || data.stepInfoList.length 0) return false; const first data.stepInfoList[0]; if (typeof first.step ! number || first.step 0) return false; if (!Number.isInteger(first.timestamp) || first.timestamp 1500000000) return false; return true; };3.3 配置文件config.js中的四个必改字段config.js是服务端的唯一配置入口部署前必须人工核对以下四项其他字段可保持默认module.exports { // 【必改】微信公众号/小程序的 AppID用于调用 auth.code2Session appId: wx1234567890abcdef, // 【必改】对应 AppID 的 AppSecret务必保管好切勿提交至 Git appSecret: a1b2c3d4e5f678901234567890abcdef, // 【必改】服务端缓存 session_key 的有效期毫秒建议 72000002 小时 tokenCacheTTL: 7200000, // 【必改】你的小程序服务器域名需在微信公众平台「开发管理 → 服务器域名」中备案 // 例如https://api.your-domain.com serverDomain: https://api.example.com };注意appSecret绝对不可硬编码在代码中。生产环境应通过环境变量注入如process.env.APP_SECRET本包为简化演示保留明文上线前必须替换为环境变量读取逻辑。4. 避坑指南五个真实踩过的坑每一条都附带复现方式与修复命令这些不是理论推测而是某开发者在模拟项目X中用真机iOS 17.4 / Android 14、不同微信版本8.0.45 ~ 8.0.52、多种网络环境WiFi / 4G / 弱网反复验证后总结的血泪经验。每一条都对应一个具体现象、根本原因和一行可执行的修复命令。4.1 现象wx.getWeRunData回调 success但res.encryptedData为空字符串原因用户未在微信「我 → 设置 → 隐私 → 运动数据」中开启“向第三方应用开放运动数据”。该开关独立于wx.authorize且微信不会主动提示。解决引导用户手动开启。可在wx.authorizesuccess 后插入一段提示wx.showModal({ title: 请开启运动数据权限, content: 请前往微信【我 → 设置 → 隐私 → 运动数据】开启“向第三方应用开放运动数据”, showCancel: false, confirmText: 去开启 });4.2 现象服务端解密返回null日志显示Error: Invalid key length原因session_key存储格式错误。微信返回的session_key是 32 字节二进制但很多开发者误存为 base64 字符串24 字节解密时却用Buffer.from(sessionKey, base64)解析导致 key 长度为 24 字节而非 32 字节。解决统一使用 hex 格式存储session_key。若已存 base64执行以下转换Node.js 环境# 假设旧 session_key 为 YmFzZTY0LWVuY29kZWQtc2Vzc2lvbmtleQ node -e console.log(Buffer.from(YmFzZTY0LWVuY29kZWQtc2Vzc2lvbmtleQ, base64).toString(hex)) # 输出6261736536342d656e636f6465642d73657373696f6e6b6579将输出的 hex 字符串64 位存入数据库并在解密时用Buffer.from(sessionKey, hex)。4.3 现象解密成功但stepInfoList为空数组或step为 0原因微信运动数据每日仅更新一次且以自然日0 点为界。wx.getWeRunData返回的是“最近一次同步的数据”若用户当天未打开微信运动或未在微信内同步过该数组即为空。解决不报错改用兜底逻辑。在服务端解密后if (!data.stepInfoList || data.stepInfoList.length 0) { return { code: 0, data: { step: 0, timestamp: Math.floor(Date.now() / 1000) } }; }4.4 现象iOS 真机正常Android 真机encryptedData为undefined原因Android 微信 8.0.48 版本存在一个兼容性 Bug当小程序未在后台“保活”时wx.getWeRunData的 success 回调中encryptedData字段丢失。解决强制触发一次wx.getBackgroundAudioPlayerState无副作用的保活 API// 在 fetchWeRunData 函数开头添加 wx.getBackgroundAudioPlayerState({ success: () {}, // 仅用于保活无需处理 fail: () {} // 失败也忽略 });4.5 现象解密后timestamp比当前时间早 8 小时如显示 2024-04-05 08:00:00实际是 16:00:00原因微信返回的timestamp是 Unix 时间戳UTC 0 时区而new Date(timestamp * 1000)在浏览器中会自动转为本地时区。中国用户看到的就是 UTC8所以显示时间比 UTC 早 8 小时。解决若需显示“今天几点”应统一转为北京时间const bjTime new Date((latest.timestamp 8 * 3600) * 1000); // 或更严谨使用 Intl.DateTimeFormat const formatter new Intl.DateTimeFormat(zh-CN, { year: numeric, month: 2-digit, day: 2-digit, hour: 2-digit, minute: 2-digit, second: 2-digit, timeZone: Asia/Shanghai }); formatter.format(new Date(latest.timestamp * 1000));5. 本地验证与线上联调用三步法确认你的部署完全正确部署不是 copy-paste 就完事。真正的闭环是你能在本地复现微信官方测试用例并在线上环境拿到自己手机的真实步数。以下三步缺一不可每一步都有可量化的验证指标。5.1 第一步用sample-data.json本地跑通解密函数/test/sample-data.json是微信官方提供的标准测试集包含一组已知输入与期望输出。它不是示意而是权威基准。执行以下命令验证你的decrypt.js是否 100% 符合规范cd server node -e const { decryptWeRunData } require(./decrypt); const testData require(../test/sample-data.json); const result decryptWeRunData( testData.encryptedData, testData.iv, testData.sessionKey ); console.log(✅ 解密成功:, result.stepInfoList[0].step 12345); console.log(✅ 时间戳正确:, result.stepInfoList[0].timestamp 1500000000); 预期输出两行✅。若任一为false说明你的解密逻辑存在偏差常见于 IV 处理、padding 启用、key 解析方式。此时不要继续下一步回到 2.2 节逐行比对代码。5.2 第二步小程序真机抓包确认encryptedData与iv原始值本地验证通过后必须用真机确认小程序端发出的数据是否“干净”。在 iOS 微信中打开「设置 → 关于微信 → 开启调试」然后在小程序中触发授权与获取观察 Network 面板中/api/decrypt-werun请求的 payload字段正确特征错误示例验证命令Linux/macOSencryptedData长度 ≥ 120字符集为 A-Z a-z 0-9 / 空格、undefinedecho $encryptedData | wc -civ长度恒为 24base64 编码123数字、abc字母echo $iv | wc -copenid长度 28以o开头12345、adminecho $openid | grep -E ^o[A-Za-z0-9]{27}$提示Android 微信不支持内置调试可改用 Charles Proxy 抓包或在uploadToServer中console.log原始参数后截图。5.3 第三步线上环境端到端联调记录三次有效步数最后一步也是最关键的验收步骤用你自己的微信账号在已上线的小程序中完成全流程并记录三次不同时间点的步数交叉验证一致性。时间点小程序显示步数微信运动 App 显示步数是否一致备注T0首次进入84218421✅首次授权后立即获取T12 小时后92159215✅确认 session_key 未过期T2次日 0 点后1203412034✅验证自然日更新机制关键技巧为排除缓存干扰每次测试前清空小程序本地缓存开发者工具 → Storage → Clear并重启微信。若三次均一致则证明你的部署已通过生产环境检验。从那以后我每次上线新版本都强制走一遍这三步先跑sample-data.json再真机抓包看原始字段最后用自己的手机测三次步数。不是信不过代码是信不过“我以为没问题”这种心态。希望帮到你。本文还有配套的精品资源点击获取