微信小程序获取运动步数完整解决方案(wx.getWeRunData 解密)
## 前言
在开发微信小程序时,获取用户的微信运动步数是一个常见需求。但微信运动数据wx.getWeRunData)返回的是加密数据,需要在后端使用 session_key 进行解密。本文将分享一个经过实战验证的完整解决方案,包括前端调用和后端 PHP 解密的核心代码。
## 技术原理
微信小程序的数据加密机制:
- 加密算法:AES-128-CBC
- 密钥:通过 wx.login() 获取的 code 换取 session_key
- 数据结构:加密数据 + PKCS#7 填充
关键点codeencryptedDataiv 必须在同一次登录会话中获取,否则解密会失败。
## 实现步骤
### 1. 前端:小程序端(核心代码)
// 获取微信运动步数的完整流程
getWeRunData() {
const that = this;
// 第一步:检查微信运动授权
wx.getSetting({
success(res) {
if (res.authSetting['scope.werun']) {
// 已授权,直接获取
that.fetchWeRunData();
} else {
// 请求授权
wx.authorize({
scope: 'scope.werun',
success() {
that.fetchWeRunData();
},
fail() {
wx.showToast({
title: '需要授权微信运动才能获取步数',
icon: 'none'
});
}
});
}
}
});
},
// 核心:登录 → 获取加密数据 → 发送后端
fetchWeRunData() {
const that = this;
// Step 1: 获取登录凭证 code
wx.login({
success(loginRes) {
if (!loginRes.code) {
wx.showToast({ title: '登录失败', icon: 'none' });
return;
}
// Step 2: 立即获取微信运动加密数据(关键:必须在同一会话)
wx.getWeRunData({
success(werunRes) {
const { encryptedData, iv } = werunRes;
// Step 3: 发送到后端解密
wx.request({
url: 'https://your-api.com/api/werun/decrypt',
method: 'POST',
data: {
code: loginRes.code,
encryptedData: encryptedData,
iv: iv
},
success(res) {
if (res.data.code === 0) {
const stepList = res.data.data.stepInfoList;
const latestStep = stepList[0]; // 最新一天的步数
console.log('今日步数:', latestStep.step);
console.log('时间戳:', latestStep.timestamp);
} else {
wx.showToast({
title: res.data.msg || '解密失败',
icon: 'none'
});
}
},
fail(err) {
wx.showToast({
title: '网络请求失败',
icon: 'none'
});
}
});
},
fail(err) {
wx.showToast({
title: '获取运动数据失败',
icon: 'none'
});
}
});
}
});
}### 2. 后端:PHP 解密(核心代码)
<?php
/**
* 微信小程序运动步数解密接口
*/
class WeRunController
{
private $appId = 'your_app_id';
private $appSecret = 'your_app_secret';
/**
* 主接口
*/
public function decrypt(Request $request)
{
$code = $request->post('code');
$encryptedData = $request->post('encryptedData');
$iv = $request->post('iv');
if (empty($code) || empty($encryptedData) || empty($iv)) {
return $this->error('参数缺失');
}
// 1. 获取 session_key
$session = $this->getSessionKey($code);
if (!$session || empty($session['session_key'])) {
return $this->error('获取登录态失败');
}
// 2. 解密数据
$result = $this->decryptData($encryptedData, $session['session_key'], $iv);
if ($result === false) {
return $this->error('解密失败');
}
return $this->success($result);
}
/**
* 获取 session_key
*/
private function getSessionKey($code)
{
$url = 'https://api.weixin.qq.com/sns/jscode2session';
$params = [
'appid' => $this->appId,
'secret' => $this->appSecret,
'js_code' => $code,
'grant_type' => 'authorization_code'
];
$response = file_get_contents($url . '?' . http_build_query($params));
return json_decode($response, true);
}
/**
* AES-128-CBC 解密
*/
private function decryptData($encryptedData, $sessionKey, $iv)
{
// Base64 解码(关键:三个参数都要解码)
$sessionKeyDecoded = base64_decode($sessionKey, true);
$encryptedDataDecoded = base64_decode($encryptedData, true);
$ivDecoded = base64_decode($iv, true);
// 校验解码结果
if ($sessionKeyDecoded === false ||
$encryptedDataDecoded === false ||
$ivDecoded === false) {
return false;
}
// 校验长度(AES-128 要求 key 和 iv 都是 16 字节)
if (strlen($sessionKeyDecoded) !== 16 || strlen($ivDecoded) !== 16) {
return false;
}
// AES-128-CBC 解密
$decrypted = openssl_decrypt(
$encryptedDataDecoded,
'AES-128-CBC',
$sessionKeyDecoded,
OPENSSL_RAW_DATA,
$ivDecoded
);
if ($decrypted === false) {
return false;
}
// 去除 PKCS#7 填充
$pad = ord(substr($decrypted, -1));
if ($pad >= 1 && $pad <= 16) {
$decrypted = substr($decrypted, 0, -$pad);
}
// 解析 JSON
$data = json_decode($decrypted, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return false;
}
// 可选:校验 appid
if (isset($data['watermark']['appid']) &&
$data['watermark']['appid'] !== $this->appId) {
return false;
}
return $data;
}
}## 关键技术点
### 1. 前端调用顺序
必须严格按照以下顺序执行:
wx.login() → wx.getWeRunData() → wx.request()在同一个回调链中完成,确保 code 和加密数据是同一次会话。
### 2. 后端解密步骤
1. Base64 解码sessionKeyencryptedDataiv 都需要解码
2. 长度校验:解码后 sessionKey 和 iv 必须是 16 字节
3. AES 解密:使用 openssl_decrypt 进行 AES-128-CBC 解密
4. 去除填充:去除 PKCS#7 填充(最后 1-16 字节)
5. JSON 解析:解析得到运动数据
### 3. 返回数据结构
{
"stepInfoList": [
{
"timestamp": 1735142400, // 时间戳(秒)
"step": 12345 // 步数
}
],
"watermark": {
"appid": "wx636346c6c2761890",
"timestamp": 1735185123
}
}## 常见错误及解决方案
### 1. 错误invalid key length
原因sessionKey 未正确解码,或长度不是 16 字节
解决:确保 base64_decode($sessionKey) 后长度为 16
### 2. 错误:填充值异常(pad > 16)
原因:
- code 和 encryptedData 不是同一会话
- sessionKey 或 iv 解码错误
解决:
- 确保前端在同一回调链中获取 code 和 encryptedData
- 检查三个参数的 base64 解码是否成功
### 3. 错误:JSON 解析失败
原因:PKCS#7 填充未正确去除
解决:检查填充值是否在 1-16 范围内,正确截取字符串
### 4. 错误:appid 校验失败
原因:使用的 appId 与小程序不匹配
解决:确认后端配置的 appId 和 appSecret 正确
## 注意事项
1. 安全性:不要在前端存储或打印 session_key
2. 时效性code 只能使用一次,且有效期 5 分钟
3. session_key 有效期:一般情况下不会过期,除非用户重新授权
4. 授权:首次获取需要用户授权 scope.werun
5. 测试:建议在真机上测试,开发者工具可能有差异
## 总结
微信小程序运动步数解密的核心要点:
1. 前端确保在同一会话中获取 code 和加密数据
2. 后端正确进行 Base64 解码和 AES-128-CBC 解密
3. 注意 PKCS#7 填充的处理
4. 做好错误处理和日志记录
通过本文的方案,你可以快速实现微信小程序运动步数的获取和解密功能。如果遇到问题,重点检查前端调用顺序和后端解密步骤。