网页手机号抓取程序(微信服务器使用code换取sessionKey加密时使用的sessionKey一致才可解密)
优采云 发布时间: 2021-11-05 13:14网页手机号抓取程序(微信服务器使用code换取sessionKey加密时使用的sessionKey一致才可解密)
最近在做一个小的toC程序,把之前做的东西捡起来。但是我发现了一些之前踩过的坑,我又踩了一遍。
实在想不起来了~花点时间做个笔记。
重点说明:要获取微信用户绑定的手机号,需要先调用wx.login接口。
原理:小程序获取的手机号码信息是微信服务器加密后的信息。微信服务器会根据wx.login下发的临时码对应的sessionKey对手机号信息进行加密。使用开发者服务器上的代码交换sessionKey,交换的sessionKey与加密信息时使用的sessionKey一致,才能解密成功。
注意:服务端使用code交换的sessionKey不是加密时使用的sessionKey,导致解密失败。建议开发者提前登录。
一、 登录授权要求
由于业务限制,用户需要在登录小程序前对其手机号码进行授权,才能进入小程序。
前端读取App.js入口文件中的本地存储,判断是否有手机号。如果有手机号,则直接刷新用户信息登录,如果没有,则需要通过登录授权流程登录。
二、 公司内部小程序19年开发,登录授权设计。后台设计**两个界面**,登录和注册界面,更新用户信息界面。登录和注册接口逻辑:
1、根据输入的参数code,调用微信auth.code2Session接口换取openId和session_key。
2、 交换成功,查询user表中是否存在输入参数openId,如果存在则更新session_key,如果不存在则创建用户并保存openId和session_key。处理完毕后,将openId返回给前端。
前端调用:
在登录页面的onShow生命周期中,会调用该接口,将返回的数据记录在页面状态中。
onShow: function () {
let that = this;
// 判断是否缓存手机号
that.data.purePhoneNumber = wx.getStorageSync("purePhoneNumber");
if (that.data.purePhoneNumber == '') {
wx.login({
success(res) {
WXrequest.post({
url: '/code2Session',
data: {
'jsCode': res.code
}
}).then(res => {
wx.setStorageSync('openid', res.data.openid);
that.data.openid = res.data.openid;
});
}
});
} else {
wx.showLoading({
title: '登录中' // 数据请求前loading
})
wx.switchTab({
url: '../mine/index'
})
}
}
更新用户信息接口逻辑:
1、 根据输入参数openId到user表查询session_key,结合输入参数encryptedData和iv解密得到手机号码。
2、手机号解密后,更新用户信息,将用户信息返回给前端。
前端调用:
用户在获取手机号码后主动触发登录按钮并调用。
getPhoneNumber: function (e) {
let that = this;
if (e.detail.errMsg == 'getPhoneNumber:ok') {
WXrequest.post({
url: '/getWXUserPhone',
data: {
'encryptedData': e.detail.encryptedData,
'openid': that.data.openid,
'iv': e.detail.iv,
}
}).then(res => {
// 存用户手机号及用户信息
wx.setStorageSync('purePhoneNumber', res.data.purePhoneNumber);
wx.setStorageSync('userInfo', res.data);
// 跳转首页
wx.showLoading({
title: '登录中'
})
setTimeout(function () {
wx.switchTab({
url: '../mine/index'
})
}, 1000)
})
}
}
注意:该代码的有效期为五分钟。代码是在登录页面的onShow生命周期中获取的。如果用户在 5 分钟后打开页面并授权电话号码,则解密将失败。需要优化。
二、 最近开发的toC小程序登录授权设计。后台同事将小程序登录+获取手机号设计成一个界面,叫做登录授权界面。接口逻辑:
1、 根据输入参数code,先调用auth.code2Session接口,换取openId和session_key。
2、 根据上一步交换的appId、encryptedData、session_key对数据进行解密,得到手机号码。
3、查询用户表中是否存在openId,并进行逻辑处理。
存在,判断用户手机号码与解析出的手机号码是否一致,一致接口返回用户信息;如果不一致,则更新手机号码,并将之前的手机号码记录在历史记录中。
如果不存在,直接创建用户并返回用户信息。
前端调用:
由于代码,有效期为五分钟。获取登录页面onShow生命周期中的code并不能保证用户会在五分钟内对手机号码进行授权。
所以获取手机号成功后,调用wx.login()获取code,然后调用登录授权接口。
问题:
先获取手机号,然后调用wx.login(),导致后台解析手机号失败,报错:session_key is invalid。
根据以往的经验,我开始直接写代码,时间太长忘记了一些注意事项。报错后再次阅读文档。文档明确提醒:要获取微信用户绑定的手机号,需要先调用wx.login接口。
注:阅读以下文档,了解微信对开放数据的验证解密,对为什么需要在获取手机号之前调用wx.login接口有更深的印象。
解决方案:
在登录页面的onShow生命周期中,每隔几分钟刷新一次登录状态。记录页面状态下的状态,获取手机号成功后发送到后台。
componentDidShow() {
// 隐藏房子
Taro.hideHomeButton();
// code 用户登录凭证(有效期五分钟), 停留在当前页面每隔两分钟,重新刷新登陆态,否则后台解析session_key会失效
Taro.login({
success: (res) => {
this.setState({
code: res.code,
});
},
});
this.timer = setInterval(() => {
Taro.login({
success: (res) => {
this.setState({
code: res.code,
});
},
});
}, 60000 * 2);
}
// 获取手机号
getPhoneNumber(e) {
if (e.detail.errMsg.indexOf("getPhoneNumber:fail") != -1) {
if (e.detail.errMsg.indexOf("user cancel") != -1) {
toast("请不要重复点击、以免取消微信授权");
} else {
toast("允许授权将获得更好的服务");
}
} else {
this.setState({
loading: true,
});
Taro.request({
url: `${config.baseUrl}/user`,
method: "POST",
header: { "Content-Type": "application/json" },
data: {
code: this.state.code,
iv: e.detail.iv,
encryptedData: e.detail.encryptedData,
},
success: (res) => {
const redData = res.data.data || {};
if (res.data.code != 0) {
toast(res.data.msg);
} else {
Taro.setStorageSync(`${config.env}openId`, redData.openId);
Taro.setStorageSync(`${config.env}userId`, redData.userId);
Taro.setStorageSync(`${config.env}phone`, redData.phone);
this.props.dispatch({
type: "User/setUser",
payload: {
userInfo: redData,
},
});
this.setState({
loading: false,
});
}
},
fail: (err) => {
this.setState({
loading: false,
});
toast(err);
},
});
}
}
// DOM
this.getPhoneNumber(e)}
>
{this.state.loading ? "登录中" : "授权手机号"}
三、详细了解微信登录授权机制原理。
踩坑后,重新研究了OAuth2.0安全协议、小程序登录授权、开放数据验证解密等流程及底层原理。
稍后花时间在笔记中组织它......