話不多是,直接上代碼(我里面按鈕用到了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 必須是企業的,且需要完成微信認真,然后你是當前項目的開發人員。
