微信小程序获取手机号登录

我睡着的时候不困唉大约 3 分钟UniApp登录微信小程序微信小程序授权

微信小程序获取手机号登录

旧版登录

登录流程图

登录操作步骤

  1. 因为需要用户主动触发才能发起获取手机号接口,需用 button 组件的点击来触发
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
getPhoneNumber (e) {
  console.log(e.detail.code)
  console.log(e.detail.errMsg)
  console.log(e.detail.iv) //加密算法的初始向量
  console.log(e.detail.encryptedData)//包括敏感数据在内的完整用户信息的加密数据
}
  1. 获取微信用户绑定的手机号,调用 uni.login() 获取 code 值 对应微信 wx.login()
// uniapp
getPhoneNumber (e) {
  //....
  uni.login({
    provider: 'weixin',
    success: (res) => {
      console.log(res.code)
    }
  })
}

//微信
getPhoneNumber (e) {
  //...
  wx.login({
    success(res) {
      console.log(res.code)
    }
  })
}
  1. 发送请求到业务系统后端,使用 code 换取 openid、unionid、session_key 等信息

    getPhoneNumber (e) {
      //....
      let _this = this
      uni.login({
        provider: 'weixin',
        success: (res) => {
          // 发送 res.code 到后台换取 openId, sessionKey, unionId
          let opParams = {}
          _this.$http.post('', opParams, function (code_res) {
            //TODO:存储openId, sessionKey, unionId到本地缓存
          })
        }
      })
    }
    
  2. 根据 getPhoneNumber 获取的初始加密值和 openId, sessionKey 获取手机号 后端


public JSONObject decodeWxAppPhoneService(String encrypted, String iv, String code) {

  try{
//	  		JSONObject json = JSONObject.fromObject(new UserInfoController().sendPost(WX_APPID, WX_SECRET, code, "authorization_code"));
//	          String jsonStr = EntityUtils.toString(response.getEntity());
//	          JSONObject jsonObject = JSON.parseObject(jsonStr);
    JSONObject json = JSONObject.fromObject(code);
    String sessionkey = json.getString("session_key");

    // 解密
    byte[] encrypData = Base64Utils.decodeFromString(encrypted);
    byte[] ivData = Base64Utils.decodeFromString(iv);
    byte[] sessionKey = Base64Utils.decodeFromString(sessionkey);
    AlgorithmParameterSpec ivSpec = new IvParameterSpec(ivData);
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    SecretKeySpec keySpec = new SecretKeySpec(sessionKey, "AES");
    cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);

    String resultString = new String(cipher.doFinal(encrypData), "UTF-8");
    JSONObject object = JSONObject.fromObject(resultString);
    // 拿到手机号码
    String phone = object.getString("phoneNumber");
    // 返回手机号码
    JSONObject returnObject = new JSONObject();
    returnObject.put("phone", phone);
    return returnObject;
  } catch (Exception e) {
    System.out.println("微信小程序手机号码解密异常,信息如下:");
    e.printStackTrace();
  }
  return null;
}

前端

getPhoneNumber (e) {
  //....
  let _this = this
  uni.login({
    provider: 'weixin',
    success: (res) => {
      // 发送 res.code 到后台换取 openId, sessionKey, unionId
      let opParams = {}
      _this.$http.post('', opParams, function (code_res) {
        //TODO:存储openId, sessionKey, unionId到本地缓存

        let params = {
          session_key: wx.getStorageSync('session_key'),
          //为了让手机号码和用户的唯一标识身份的openId关联起来所以才传的openId
          wxOpenid: wx.getStorageSync('openId'),
          encryptedData: e.detail.encryptedData,
          iv: e.detail.iv,
        }
        // 发送请求获取手机号
        _this.$http.post('', params, function (code_res) {})
      })
    }
  })
}

最终代码

ongetPhoneNumber(e) {
  /*
    1.获取微信小程序认证appId和secret
    2.后端根据appId和secret,code(前端获取),后端调用
    "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s
    返回openId, sessionKey, unionId
    3.前端拼接参数 encryptedData,iv,openId,sessionKey,后端调用微信小程序
    "https://api.weixin.qq.com/wxa/business/getuserphonenumber" + 拼接参数,返回手机号,
    4.前端存储手机号、根据手机号调用登录接口
  */
  let _this = this
  if (e.detail.errMsg == 'getPhoneNumber:fail:user deny') {
    uni.showToast({
      title: '您拒绝了授权,将不能正常使用小程序',
      icon: 'error',
      duration: 3000
    })
  } else {
    // 调用 action ,请求登录接口
    uni.login({
      provider: 'weixin',
      success: (res) => {
        //获取到code
        _this.wxCode = res.code
        //请求登录接口
        if (res.errMsg == 'login:ok') {
          let params = {
            code: _this.wxCode
          }
          console.log('res3 :>> ', res)
          // 发送 res.code 到后台换取 openId, sessionKey, unionId
          _this.$http.post('', params, function (code_res) {
            wx.setStorageSync('openId', code_res.data.data.openid)
            wx.setStorageSync('session_key', code_res.data.data.session_key)
            wx.setStorageSync('unionId', code_res.data.data.wxUid)
            // 获取手机号
            let insertUserPhoneData = {
              //足够解密手机号使用了start
              session_key: wx.getStorageSync('session_key'),
              encryptedData: e.detail.encryptedData,
              iv: e.detail.iv,
              //足够解密手机号使用了end
              //为了让手机号码和用户的唯一标识身份的openId关联起来所以才传的openId
              wxOpenid: wx.getStorageSync('openId'),
              //这个learnerCode是业务需要,你们可以不要
              learnerCode: wx.getStorageSync('learnerCode')
            }
            // 发送请求获取手机号
            _this.$http.post('', insertUserPhoneData, function (phone_res) {
              console.log('phone_res :>> ', phone_res)
              let loginParams = {}
              // TODO:具体登录接口发送
              that.$http.post(
                '',
                loginParams,
                function (res) {
                  if (res.code == 200) {
                    uni.showToast({
                      title: '登录成功',
                      icon: 'success',
                      mask: true
                    })
                    //获取到token 存入缓存。通过有无token来判断是否登录
                    uni.setStorageSync(
                      ACCESS_TOKEN,
                      res.data.data.access_token
                    )
                  }
                },
                function (err) {
                  uni.showToast({
                    icon: 'none',
                    title: err.msg
                  })
                }
              )
            })
          })
        }
      }
    })
  }
}