uni-app小程序微信一键登录实现过程记录
话不多是,直接上代码(我里面按钮用到了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 必须是企业的,且需要完成微信认真,然后你是当前项目的开发人员。