本来写过一篇获取手机号码出错的博客:
微信小程序获取手机号解密偶尔出错:Padding is invalid and cannot be removed._我想我是海 冬天的大海 心情随风轻摆-CSDN博客
但实际上还是没有给出具体的解决方案,这里补充。
事实上,即使能用,但老是要用户试第2次,用户体验还是不会好,所以还是来点正确的姿势吧。
一、小程序端相关代码:
1. app.js
增加函数: getOpenidAndSave
//获取openid,而且保存到缓存
getOpenidAndSave(opt) {
var that = this;
wx.login({
success(res) {
console.log("getOpenidAndSave, wx.login: success, ", res);
let jsCode =res.code;
if (res.code) {
// 换取微信openId
that.post({
method: "POST",
url: "xxx/getWechatOpenId",//获取openId的接口
data: {
Value: res.code
},
success: ret => {
console.log("xxx/getWechatOpenId, success:", ret);
// 获取到微信openId
let data = JSON.parse(ret)
let openId = data.openid;
let session_key = data.session_key;
console.log("保存openid之前输出:", openId);
//保存openid
wx.setStorageSync('OPENID',openId);
if(opt && opt.success){
opt.success(openId, session_key, jsCode);
}
},
err: (res) => {
console.log("s_Wechat/getWechatOpenId, err:", res);
}
})
}else{
console.log("getOpenidAndSave, wx.login: success, 但未获取到code");
}
},
fail(res) {
console.log("getOpenidAndSave, wx.login: fail, ", res);
}
})
},
2. wxml 页面里面的按钮
注意 open-type 必须是:getPhoneNumber
<button class="btn" open-type="getPhoneNumber" bindgetphonenumber="onGetCurrMobile" >获取本机号码</button>
3. 当前页面.js?
data 里面增加 2 项
data: {
...
//一键登录2要素
session_key: '',
openId: ''
},
?onShow :
/**
* 生命周期函数--监听页面显示
*/
onShow: function () {
let that = this;
console.log('onShow');
//立即获取一次 openId 和 session_key, 便于后面的一键登录
app.getOpenidAndSave({
success: (openId,session_key,jsCode) => {
that.setData({
session_key: session_key,
openId: openId
});
}
});
},
获取手机号码按钮上的事件:
onGetCurrMobile(e){
let that = this;
if(e.detail.errMsg=="getPhoneNumber:fail user deny"){
wx.showToast({
title: '您选择了拒绝,无法使用本机认证的方式',
icon: 'none'
})
return false;
}
var para = {
EncryptedData: e.detail.encryptedData,
IV: e.detail.iv,
SessionKey: that.data.session_key,
OpenId: that.data.openId
};
//已有所有需要的参数,直接请求
app.post({
url: 'xxx/getPhoneNumber2', //接口地址
data: para,
success: function (phoneNumber) {
console.log("已获取到正确手机号=", phoneNumber);
//写上你自己的业务代码
},
err: function(res){
console.log(res);
}
});
},
二. C#后台
1. 获取本机号码的接口:
/// <summary>
/// 获取手机号V2,不会有出错
/// </summary>
/// <param name="code">code</param>
/// <returns></returns>
[HttpPost]
public MapResponse GetPhoneNumber2([FromBody] ParaWxGetPhoneNumber2 para)
{
string phoneNumber = new WechatHelper().DecryptPhoneNumber(para.EncryptedData, para.IV, para.SessionKey);
if (phoneNumber.StartsWith("Padding"))
{
phoneNumber = "意外错误,请重试";
}
//如果手机号不正确,返回的是特殊的错误信息
if (!ValidateUtil.IsMobile(phoneNumber))
{
return MapResponse.Fail(ResponseCode.SYSTEM_INVALID, phoneNumber);
}
//否则返回正确的手机号
return MapResponse.Success(phoneNumber);
}
?2. WechatHelper.cs 相关方法:
/// <summary>
/// AES解密:从小程序中 getPhoneNumber 返回值中,解析手机号码
/// </summary>
/// <param name="encryptedData">包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法</param>
/// <param name="IV">加密算法的初始向量</param>
/// <param name="Session_key"></param>
/// <returns>手机号码</returns>
public string DecryptPhoneNumber(string encryptedData, string IV, string Session_key)
{
try
{
byte[] encryData = Convert.FromBase64String(encryptedData); // strToToHexByte(text);
RijndaelManaged rijndaelCipher = new RijndaelManaged();
rijndaelCipher.Key = Convert.FromBase64String(Session_key); // Encoding.UTF8.GetBytes(AesKey);
rijndaelCipher.IV = Convert.FromBase64String(IV);// Encoding.UTF8.GetBytes(AesIV);
rijndaelCipher.Mode = CipherMode.CBC;
rijndaelCipher.Padding = PaddingMode.PKCS7;
ICryptoTransform transform = rijndaelCipher.CreateDecryptor();
byte[] plainText = transform.TransformFinalBlock(encryData, 0, encryData.Length);
string result = Encoding.Default.GetString(plainText);
//动态解析result 成对象
dynamic model = Newtonsoft.Json.Linq.JToken.Parse(result) as dynamic;
return model.phoneNumber;
}
catch (Exception ex)
{
return ex.Message;
}
}
总结一下,重点还是 onShow 那里就立即获取到?session_key 。
虽然可以在后面按钮里请求时再来获取,但那时很容易导致第1次请求出错。
为什么要获取到 openid?
如果只是获取手机号,那是用不着。但如果是 本机号码一键登录,那就有用了。
|