6.6获取用户信息
若开发者需要获取用户的个人信息(头像、昵称、性别与地区),可以调用wx.getUserProfile接口来获取。wx.getUserProfile接口只能在页面点击事件(例如 button 上 bindtap 的回调中)中才可以调用,每次调用都会弹出授权窗口,用户同意后返回 userInfo。通过wx.getUserProfile接口获取用户信息的代码如下:
WXML文件:user_info.wxml
<view class="container">
<view class="userinfo">
<block wx:if="{{!hasUserInfo}}">
<button bindtap="getUserProfile">获取头像昵称</button>
</block>
<block wx:else>
<image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" mode="cover"></image>
<text>{{userInfo.nickName}}</text>
</block>
</view>
</view>
在user_info.js文件中的getUserProfile函数是button按钮的事件处理函数,在该函数中调用了wx.getUserProfile:
Page({
data: {
userInfo: {},
hasUserInfo: false,
},
getUserProfile(e) {
wx.getUserProfile({
desc: '获取用户信息',
success: (res) => {
console.log(res)
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
})
wx.getUserProfile调用成功后返回的数据的结构如下所示:
- userInfo:用户信息对象
- rawData:不包括敏感信息的原始数据字符串,用于计算签名
- signature:使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见用户数据的签名验证和加解密
- encryptedData:包括敏感数据在内的完整用户信息的加密数据,详见用户数据的签名验证和加解密
- iv:加密算法的初始向量,详见用户数据的签名验证和加解密
- cloudID:敏感数据对应的云ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据,详细见云调用直接获取开放数据
其中userInfo为用户信息对象,以下的代码是执行wx.getUserProfile调用成功后返回数据中的userInfo信息:
avatarUrl: "https://thirdwx.qlogo.cn/mmopen/......."
city: "xxxxxx"
country: "China"
gender: 1
language: "zh_CN"
nickName: "xxxxxx"
province: "xxxxxx"
开发者若需要在界面中展示用户的头像昵称信息,也可以通过组件进行用户信息的显示,该组件无需用户确认,可以在界面中直接展示用户信息:
<view class="container">
<view class="userinfo">
<view class="userinfo-avatar" bindtap="bindViewTap">
<open-data type="userAvatarUrl"></open-data>
</view>
<open-data type="userNickName"></open-data>
</view>
</view>
6.7获取用户手机号码
用户的手机号码是属于重要的用户隐私数据,应用中获取用户手机号码的目的为了方便联系用户,例如通过手机号码给用户发送订单信息短信。微信小程序提供了一个特定的button 组件(button的open-type 的值为 getPhoneNumber)来获取手机号码。当用户点击并同意之后,可以通过 bindgetphonenumber 事件处理函数获取到微信服务器返回的加密数据。要得到最终的手机号码还需要调用业务服务器来对加密数据进行解密,业务服务器结合 session_key 以及 app_id 解密加密数据并获得手机号。
由于手机号码是比较重要的用户隐私数据,因此对于手机号码的获取有一定的限制条件:
1)获取微信用户绑定的手机号,需先调用wx.login接口。
2)因为需要用户主动触发才能发起获取手机号接口,所以手机号码不能使用API方式来获取,需用 button 组件的点击来触发获取。
3)目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。
代码示例:user_mobile.wxml
<scroll-view scroll-y="true">
<div style="display:flex;padding:10rpx">
<div>您的手机号码:</div>
<div style="flex-grow:1">{{mobile}}</div>
</div>
<div style="display:flex;padding:10rpx">
<button style="width:100%" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">获取手机号码</button>
</div>
</scroll-view>
代码示例:user_mobile.js
function aesDecode(page, data, iv) {
var domain = getApp().globalData.domain;
var url = domain + '/aes_decode';
var ssid = getApp().globalData.ssid
wx.request({
url: url,
data: {
ssid: ssid,
data: data,
iv: iv
},
success: function (res) {
if (res.statusCode === 200) {
page.setData({
mobile: res.data.phoneNumber
});
}
}
});
}
Page({
data: {
mobile: ""
},
onLoad: function (options) {
var ssdata = getApp().getLogInfo();
if(ssdata.length < 1){
getApp().wxLogin();
}
},
getPhoneNumber: function (e) {
var res = e.detail;
aesDecode(this, res.encryptedData, res.iv);
}
})
在user_mobile.js中,首先在onLoad事件处理函数中判断用户的登录状态,若没有登录需要首先登录微信。getPhoneNumber是bindgetphonenumber 事件的事件处理函数,bindgetphonenumber 事件处理函数的参数的数据结构为:
- encryptedData:包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法
- iv:加密算法的初始向量,详细见加密数据解密算法
- cloudID:敏感数据对应的云ID,开通云开发的小程序才会返回,可通过云调用直接获取开放数据,详细见云调用直接获取开放数据
调用业务服务器解密数据时,将iv、encryptedData发送给业务服务器,业务服务器使用session_key对encryptedData解密,下面的代码是解密后的数据的一个例子:
{
"phoneNumber": "13580006666",
"purePhoneNumber": "13580006666",
"countryCode": "86",
"watermark":
{
"appid":"APPID",
"timestamp": TIMESTAMP
}
}
微信开放数据的解密
小程序可以通过各种前端接口获取微信提供的开放数据,例如wx.getUserProfile接口获取用户的个人信息(头像、昵称、性别与地区)。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData)进行对称解密。解密算法如下:
1)对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
2)对称解密的目标密文为 Base64_Decode(encryptedData)。
3)对称解密秘钥 aeskey = Base64_Decode(session_key)。
4)对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
以下的代码是微信开放数据解密的相关函数:
//解除KCS#7填充
func PKCS7UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
//AES-128-CBC解密
func AesCBCDncrypt(encryptData, key, iv []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
blockSize := block.BlockSize()
if len(encryptData) < blockSize {
panic("ciphertext too short")
}
if len(encryptData)%blockSize != 0 {
panic("ciphertext is not a multiple of the block size")
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(encryptData, encryptData)
encryptData = PKCS7UnPadding(encryptData)
return encryptData, nil
}
//微信开放数据解密
func WxDncrypt(rawData, key, iv string) (string, error) {
data, err := base64.StdEncoding.DecodeString(rawData)
if err != nil {
return "", err
}
key_b, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return "", err
}
iv_b, err := base64.StdEncoding.DecodeString(iv)
if err != nil {
return "", err
}
dnData, err := AesCBCDncrypt(data, key_b, iv_b)
if err != nil {
return "", err
}
return string(dnData), nil
}