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 必须是企业的,且需要完成微信认真,然后你是当前项目的开发人员。

 

posted on 2021-12-25 17:26  -coco  阅读(3206)  评论(0编辑  收藏  举报