获取用户手机号码包含了”获取用户的昵称、头像授权”、”获取用户的手机号授权”和”解密手机号”3个部分。在小程序获取了的手机号码是加密的密文,还需要经过解密才能获取明文手机号,解密的操作放在了服务器上。
1、获取用户的昵称、头像授权。
(1)小程序前端页面代码。
Open-type:微信开发能力,值为getUserInfo时获取用户信息,可以从bindgetuserinfo回调中获取到用户信息。
(2)弹出获取昵称、头像授权窗口。
用户点击登录,弹出微信授权窗口:
点击允许,调用onGetUserInfo方法,获取用户信息:
// 通过按钮让微信用户授权以获取其信息
onGetUserInfo: function (e) {
var that = this;
wx.getUserInfo({
success(res) {
console.log(res);
if (res.errMsg == 'getUserInfo:ok') {
//将信息放入缓存
util.writeStorage(storage.WX_USER_Info, res.userInfo);
that.setData({
encrypted_UnionID: res.encryptedData,
iv_UnionID: res.iv
});
// that.obtainAuthorizationInformation();
that.setData({ showModal: true }); //显示授权手机号弹窗
} else {
wx.showToast({
title: '授权用户信息失败,请重试',
icon: 'none'
})
}
// that.setData({
// encrypted_UnionID: res.encryptedData,
// iv_UnionID: res.iv
// });
},
fail(res) {
wx.showToast({
title: '获取用户基本信息失败',
icon: 'none'
})
console.log('获取用户基本信息失败!');
util.writeStorage(storage.AUTHORISED, 0);
}
});
}
2、获取用户手机号授权。
(1)获取用户信息之后,显示准备获取授权手机号弹窗 。
if (res.errMsg == 'getUserInfo:ok') {
//将信息放入缓存
util.writeStorage(storage.WX_USER_Info, res.userInfo);
that.setData({
encrypted_UnionID: res.encryptedData,
iv_UnionID: res.iv
});
// that.obtainAuthorizationInformation();
that.setData({ showModal: true }); //显示授权手机号弹窗
}
(2)获取手机号的前端页面代码。
<view class="toGetPhoneNumberShade" wx:if="{{ showModal }}" catchtap="closeModal"></view>
<view class="toGetPhoneNumber" wx:if="{{ showModal }}">
<view>
<image src="../../images/points.png"></image>
<text wx:if="{{ showShopName }}">商家名称</text>
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">微信用户快捷登录</button>
</view>
</view>
getPhoneNumber:
获取用户手机号,可以从bindgetphonenumber回调中获取到用户信息,具体说明 (小程序插件中不能使用)。
bindgetphonenumber: 获取用户手机号回调,open-type=getPhoneNumber时有效。
点击”微信用户快捷登录”,弹出获取手机号授权窗口:
(3)获取手机号的js代码
点击允许,调用getPhoneNumber函数:
// 绑定手机号
getPhoneNumber: function (e) {
console.log("xxxxxxxxxxxxxxxx");
var that = this;
that.setData({ showModal: false }); //隐藏获取手机号弹窗;
wx.checkSession({
success: function (res) { // session_key没有过期
console.log(res);
var loginParams = {
'decryptSessionCode': that.data.decryptSessionCode, // 解密需要的参数
'encryptedUnionID': that.data.encrypted_UnionID, // 加密的unionID
'ivUnionID': that.data.iv_UnionID, // 解密需要的参数
'encryptedPhone': e.detail.encryptedData, // 加密的手机号
'ivPhone': e.detail.iv, // 解密需要的参数
'mobile': "",
'companySN': that.data.companySN
};
that.login(loginParams);
},
fail: function (res) { // session_key已过期,重新获取
console.log(res);
wx.login({
success(res) {
that.setData({ decryptSessionCode: res.code });
},
fail(res) { // 这里需要处理调用接口失败的情况
console.log('调用wx.login失败');
}
});
wx.showToast({
title: '授权手机号失败,请重试',
icon: 'none'
})
}
});
},
设置请求登录变量loginParams,然后调用login函数。
(此时获取的手机号还是加密的手机号encryptedPhone,解密的操作放在了服务器端,需要把decryptSessionCode、encryptedPhone、ivPhone几个参数传到服务器端。)
3、解密用户的手机号。
解密手机号的操作在服务器端进行。
(1)请求服务器解密手机号码的js代码。
// 第一次登录,非自动登录
login: function (loginParams) {
// let header = wx.getStorageSync('storageHeader'); // TODO 需要处理nbr会话失效的情况
let that = this;
wx.showLoading({
title: '登录中,请稍等',
mask: true
})
wx.request({
url: api.login,
method: api.POST,
header: app.globalData.header,
data: loginParams,
success(res) {
console.log(res);
console.log(res.data.objectList2);
util.writeStorage(storage.Company_Address, res.data.objectList2);
that.onRequestSuccess(res)
},
fail(res) { // 这里需要处理调用接口失败的情况
console.log(res);
wx.hideLoading();
wx.showToast({
title: '登录失败,请重试',
icon: 'none'
})
return null;
}
});
},
(2)后端服务器解密手机号。
(2.1)向微信服务器获取sessionKey。
根据SessionAndOpenid_URL、appid、secret、decryptSessionCode向微信服务器发送请求,获取sessionKey(需要sessionKey才能解密手机号):
// 向微信请求sessionKey
String sessionAndOpenidURL = String.format(SessionAndOpenid_URL, MP_APPID, MP_SECRET, vip.getDecryptSessionCode());
JSONObject jsonObject = WxUtils.getDataFromWxServer(sessionAndOpenidURL);
if (jsonObject == null) {
logger.error("获取openid和session_key失败!!!");
params.put(JSON_ERROR_KEY, EnumErrorCode.EC_OtherError.toString());
params.put(KEY_HTMLTable_Parameter_msg, "获取微信session_key失败!");
return false;
}
String sessionKey = jsonObject.getString("session_key"); // ...
if (StringUtils.isEmpty(sessionKey)) {
logger.error("获取session_key失败!");
params.put(KEY_HTMLTable_Parameter_msg, "获取session_key失败!");
params.put(JSON_ERROR_KEY, EnumErrorCode.EC_OtherError.toString());
return false;
} else {
logger.debug("获取的sessionKey:" + sessionKey);
}
getDataFromWxServer方法代码:
/** 通用函数。 向微信服务器发送Get请求,返回JSON数据。 */
public static JSONObject getDataFromWxServer(String url) {
HttpClient httpClient = HttpClientBuilder.create().build();
try {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet);// 接收client执行的结果
HttpEntity entity = response.getEntity();
if (entity != null) {
String result = EntityUtils.toString(entity, "UTF-8");
return JSONObject.fromObject(result);
} else {
logger.error("向微信服务器发送Get请求发生错误!");
return null;
}
} catch (Exception e) {
logger.error("向微信服务器发送Get请求发生错误:" + e.getMessage());
return null;
}
}
(2.2)解密手机号。
根据sessionKey还有小程序传过来的密文手机号encryptedPhone、ivPhone进行解密:
String mobile = decryptData(vip.getEncryptedPhone(), vip.getIvPhone(), sessionKey).getString("phoneNumber");
if (StringUtils.isEmpty(mobile) || mobile.length() != FieldFormat.LENGTH_Mobile) {
logger.error("解密手机号失败!");
params.put(KEY_HTMLTable_Parameter_msg, "网络异常,请稍后再试!");
params.put(JSON_ERROR_KEY, EnumErrorCode.EC_OtherError.toString());
return false;
}
这样就可以得到明文的手机号码 mobile 了。
decryptData方法代码:
private JSONObject decryptData(String encryptedData, String ivData, String session_key) {
byte[] encrypted = Base64.decodeBase64(encryptedData);
byte[] iv = Base64.decodeBase64(ivData);
byte[] key = Base64.decodeBase64(session_key);
AESUtil aesUtil = new AESUtil(key, iv);
return JSONObject.fromObject(aesUtil.decrypt(encrypted));
}