話不多是,直接上代碼(我里面按鈕用到了vant的組件):
<template> <view style="height: 100vh;display: flex;flex-direction: column;align-items: center;justify-content: center;box-sizing: border-box;"> <van-button type="primary" block open-type="getUserInfo" @getuserinfo="appLoginWx"> 微信一鍵登錄 </van-button> <van-button v-show="showPhone" type="primary" block open-type="getPhoneNumber" @getphonenumber="getPhone"> 授權電話 </van-button> </view> </template> <script> const WXBizDataCrypt = require('@/utils/WXBizDataCrypt') export default { data() { return { appId:'******************', // 小程序的appId(注意需要企業的appId,並且你是開發人員,不然后續無法獲取到手機號碼) showPhone: false, userInfo: {}, sessionKey: null, unionid: null, openId: null, phone: null } }, methods: { // 授權獲取微信用戶信息 appLoginWx(res) { console.log(res) // 獲取用戶信息成功 if (res.detail.errMsg === 'getUserProfile:ok' || res.detail.errMsg === 'getUserInfo:ok') {this.userInfo = res.detail.userInfo; let _this = this; uni.showLoading({ title: '獲取用戶信息...', mask: true }); // 獲取jsCode uni.login({ provider: 'weixin', success(resLogin) { console.log(resLogin); if (resLogin.errMsg === "login:ok") { // 獲取jsCode成功,通過jsCode調用微信api獲取用戶openId _this.wechatLogin(resLogin.code); return } uni.hideLoading(); uni.showToast({ title: '獲取用戶信息失敗,請重試', icon: 'none', duration: 2000 }); }, fail() { uni.hideLoading(); uni.showToast({ title: '獲取用戶信息失敗,請重試', icon: 'none', duration: 2000 }); } }); } }, wechatLogin(wxCode) { // 這里一般讓后端去調用微信api獲取openId等信息,前端調用的話像會多下面這個請求 // 因為若果是后端去調用的話,后端拿到微信api返回的openId后可直接去數據庫查詢判斷該用戶是否已注冊, // 如果已經注冊了就直接返回注冊的用戶信息, // 如果未注冊,則后端需要返回openId、sessionKeyde等信息, // 然后前端頁面彈出授權獲取手機號碼的按鈕,用戶點擊后拿到手機號后進行解密后再把信息提交給后台進行注冊 const params = { appid: this.appId, // 小程序的appId(注意需要企業的appId,並且你目前是開發人員,不然后續無法獲取到手機號碼) secret: '***************', // 企業小程序的secret js_code: wxCode, // 注意jsCode動態獲取的,使用一次后將失效 grant_type: 'authorization_code' // 這個參數固定寫死 } // 調用微信api獲取openid等信息 this.$get('https://api.weixin.qq.com/sns/jscode2session', params).then(res => { console.log(res) if (res.errcode) { console.log('獲取openId失敗') uni.hideLoading(); uni.showToast({ title: '獲取用戶信息失敗', icon: 'none', duration: 2000 }); return } this.openId = res.openid this.sessionKey = res.session_key this.unionid = res.unionid // 調用后端接口判斷用戶是否已注冊過 this.$post('/login', { openId: this.openId }).then(res => { uni.hideLoading(); // 若返回的參數中有token說明已經注冊過 if (res.status === 200 && res.data.token) { uni.showToast({ title: '登錄成功!', duration: 1000 }); // 緩存用戶信息 uni.setStorageSync("token", res.data.token); uni.setStorageSync("userInfo", res.data); // 登錄成功返回上一頁 setTimeout(() => { uni.navigateBack({ delta: 1 }) }, 1000) return } // 走到這里說明沒有注冊過,需要彈出授權獲取用戶手機號的彈框按鈕讓用戶授權 if (res.status == 200) { this.showPhone = true return } uni.showToast({ title: res.message, icon: 'none', duration: 2000 }); }).catch(() => { uni.hideLoading(); uni.showToast({ title: '登錄失敗,請稍后重試', icon: 'none', duration: 2000 }); }) }).catch(err => { console.log(err) uni.hideLoading(); uni.showToast({ title: '獲取用戶信息失敗', icon: 'none', duration: 2000 }); }) // 下面注釋的就是后端去調用微信api獲取用戶openId的提供給前端的接口 // this.$post('/login', { // jsCode: wxCode // }).then(res => { // console.log(res); // uni.hideLoading(); // // 若直接返回token說明已經注冊 // if (res.status === 200 && res.data.token) { // uni.showToast({ // title: '登錄成功!', // duration: 1000 // }); // uni.setStorageSync("token", res.data.token); // this.saveUserInfo(res.data); // setTimeout(() => { // // this.goTabBar('/pages/index') // uni.navigateBack({ // delta: 1 // }) // }, 1000) // return // } // if (res.status == 200) { // this.openId = res.data.openid // this.sessionKey = res.data.session_key // this.unionid = res.data.unionid // this.id = res.data.id // this.showPhone = true // return // } // uni.showToast({ // title: res.message, // icon: 'none', // duration: 2000 // }); // }).catch(() => { // uni.hideLoading(); // uni.showToast({ // title: '登錄失敗,請稍后重試', // icon: 'none', // duration: 2000 // }); // }) }, // 授權電話號回調 (這里千萬注意:小程序必須要完成 微信認證才能獲取到加密的手機號碼) getPhone(res) { console.log(res) if (res.detail.errMsg === 'getPhoneNumber:ok') { const encryptedData = res.detail.encryptedData console.log(123, sessionKey, encryptedData, iv) const pc = new WXBizDataCrypt(this.appId, this.sessionKey) const data = pc.decryptData(encryptedData, res.detail.iv) // 這里使用解密手機號的方法 console.log('解密后 data: ', data.phoneNumber) this.phone = data.phoneNumber this.showPhone = false this.loginHandle() } else { this.showPhone = false uni.showToast({ title: '獲取手機號失敗,請重試', icon: 'none', duration: 2000 }); } }, // 用戶注冊 loginHandle() { const data = { "account": this.phone, "icon": this.userInfo.avatarUrl, "id": this.id, "nickname": this.userInfo.nickName, "openId": this.openId, "phone": this.phone, "unionid": this.unionid } console.log(data) uni.showLoading({ title: '登錄中...', mask: true }); this.$post('/login/login', data).then(res => { console.log(res); uni.hideLoading(); if (res.status == 200) { uni.showToast({ title: '登錄成功!', duration: 1000 }); uni.setStorageSync("token", res.data.token); uni.setStorageSync("userInfo", res.data); setTimeout(() => { uni.navigateBack({ delta: 1 }) }, 1000) } else { uni.showToast({ title: res.message, icon: 'none', duration: 2000 }); } }); }, } } </script> <style> </style>
以下是上面解密手機號方法的封裝,我是放在utils目錄下的:
var crypto = require('crypto') function WXBizDataCrypt(appId, sessionKey) { this.appId = appId this.sessionKey = sessionKey } WXBizDataCrypt.prototype.decryptData = function (encryptedData, iv) { // base64 decode var sessionKey = new Buffer(this.sessionKey, 'base64') encryptedData = new Buffer(encryptedData, 'base64') iv = new Buffer(iv, 'base64') try { // 解密 var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv) // 設置自動 padding 為 true,刪除填充補位 decipher.setAutoPadding(true) var decoded = decipher.update(encryptedData, 'binary', 'utf8') decoded += decipher.final('utf8') decoded = JSON.parse(decoded) } catch (err) { throw new Error('Illegal Buffer') } if (decoded.watermark.appid !== this.appId) { throw new Error('Illegal Buffer') } return decoded } module.exports = WXBizDataCrypt
注意事項:想要獲取到手機號並成功解密的話,appId 必須是企業的,且需要完成微信認真,然后你是當前項目的開發人員。