记录 "PC端采用微信扫码,进行账号注册/登录功能" 的实现过程(仅前端)

使用场景:在网站顶部导航栏的登录入口,中实现微信扫码登录功能

开发参考文档:微信官方文档(微信登录功能-网站应用微信登录开发指南)

微信提供的扫码方式有两种,分别是:

  • 跳转二维码扫描页面
  • 内嵌式二维码(本记录选用的扫码方式)

根据文档我们可以知道关于扫码授权的模式整体流程为:

1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据 code 参数;

2. 通过 code 参数加上 AppID 和AppSecret等,通过 API 换取access_token;

3. 通过access_token进行接口调用,获取用户基本数据资源或帮助用户实现基本操作。

通过 微信开放平台 申请账号拿到appId和appSecret

页面中先引入如下 JS 文件,若网站不支持https可把前缀改为http

<script src="https://res.wx.qq.com/connect/zh_CN/htmledition/js/wxLogin.js"></script>

创建登录组件:login.vue

<template>
  <div>
    <!-- 登录方式tab(非二维码登录方式) -->
    <div class="login-type" v-if="!showQR">
        ...
    </div>
    <!-- 二维码登录 -->
    <div id="login_container" v-show="showQR"></div>
  </div>
</template>

<script>
import {
  reactive,
  watch,
  toRefs,
  getCurrentInstance,
} from 'vue'
export default {
  props: {},
  setup(props, context) {
    const { appContext } = getCurrentInstance()
    const { $http } = appContext.config.globalProperties
    const state = reactive({
      showQR: true,
      loginType: 'QRcode',
      showReg: false,
      showLogin: true,
    })
    watch(
      () => state.loginType,
      newVal=> {
          state.showLogin = false
          state.showQR = false
          state.showReg = false
          switch(newVal){
            case 'QRcode':
              state.showQR = true
              break; 
            case 'register':
              state.showReg = true
              break;
            default: 
              state.showLogin = true
          }
      }
    )
    // 微信登录
    const wechatLogin = async () => {
      // 从后端获取appid以及重定向地址
      let { data, status } = await $http.get('XXX') 
      if (!status == 200) return
      // 官方文档提供的实例对象,具体参数说明可往下看
      let state = parseInt(new Date().getTime() / 1000)
      let obj = new WxLogin({
        self_redirect: false,
        id: 'login_container',
        appid: data.appid,
        scope: 'snsapi_login',
        redirect_uri: data.redirect_url, // 这里跳的是扫码成功后,账户操作的地址
        state: state,
        style: 'black',
        href: '',
      })
      // 自己封装的sessionStorage方法
      sessionStorage.setItem('beforeLogin', window.location.href) // 记录当前操作页面,以备后面跳转
      sessionStorage.setItem('QRcode', 'login') // 记录当前扫码是注册还是登录
      state.loginType = 'QRcode'
    }
    return {
      ...toRefs(state),
      wechatLogin
    }
  }
}
</script>

扫码成功后,将会自动跳转到自定义的重定向地址,并能在地址上看到我们的code

https://www.xxx.com?code=0719adHa1IuEpD0NHCHa1MQtv30&state=1655733828


接着,我们就可以根据code,用文档给出的API,获取所需的数据

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

API相关参数说明

 返回样例:

// 正确返回样例
{ 
"access_token":"ACCESS_TOKEN", 
"expires_in":7200, 
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID", 
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

// 错误返回样例
{"errcode":40029,"errmsg":"invalid code"}

 正确的返回参数说明:


在本项目我们获取code后,我们需要通过code去判断这个微信是否已经绑定账号,我们定向到操作页面,把code交给后端判断此code是否已绑定账户,并根据判断结果进行不同操作:

<template>
  <el-skeleton class="wxLogin-skeleton" :loading="!unionid" animated>
    <template #template>
      <el-skeleton :rows="2" animated />
    </template>
    <template #default>
      <el-card shadow="never" v-show="isLogin && !hasBind">
        <!-- 绑定信息表格 start -->
        ...
        <!-- 绑定信息表格 end -->

        <!-- 提交按钮 -->
        <el-row type="flex" class="row">
          <el-button class="submit-btn" @click="wxL">绑定账号</el-button>
        </el-row>
      </el-card>
      <el-card shadow="never" v-show="!isLogin || hasBind">
        <div class="waiting-box">操作成功,正在跳转,请稍等</div>
      </el-card>
    </template>
  </el-skeleton>
</template>
<script>
import {
  reactive,
  toRefs,
  getCurrentInstance
} from 'vue'
export default {
  components: {},
  setup(props, context) {
    const { appContext } = getCurrentInstance()
    const { $g, $router, $http, $message } = appContext.config.globalProperties
    const state = reactive({
      unionid: null,
      isLogin: sessionStorage.getItem('QRcode') == 'login',
      hasBind: false
    })
    // 设置用户登录信息
    const setInfo = (data, duration = 3000) => {
      let temp = data.user
      ...
      sessionStorage.removeItem('QRcode') // 移除QRcode标志
    }
    // 跳转登录页面
    const backPage = () => {
      const beforeLogin = sessionStorage.getItem('beforeLogin')
      sessionStorage.removeItem('beforeLogin')
      window.location.replace(beforeLogin)
    }
    // 第一次微信快捷登录,创建并绑定手机账号
    const wxL = async () => {
      if (!state.unionid) return $message.warning('扫码失效,请重新扫码登录')
      let { data, status } = await $http.post('xxxxx', {
        unionid: state.unionid,
        ...
      })
      if (status != 200) return
      setInfo(data)
      $message({
        message: '登录绑定成功,正在跳转,请稍等',
        type: 'success',
        duration: 1000,
        onClose: () => {
          backPage()
        }
      })
    }
    // 已有账号绑定微信
    const wxB = async () => {
      let { data, status } = await $http.post('xxxx', {
        unionid: state.unionid,
        ...
      })
      if (status != 200) return
      setInfo(data)
      $message({
        message: '绑定成功,正在跳转,请稍等',
        type: 'success',
        duration: 1000,
        onClose: () => {
          backPage()
        }
      })
    }
    // 获取unionid
    const getUnionid = async () => {
      let { data, status } = await $http.get('xxx', {
        code: $router.currentRoute.value.query.code
      })
      if (status != 200) return
      // 从未绑定
      if (!data.state) {
        state.unionid = data.unionid
        if (state.isLogin) return
        return wxB() //已有账号绑定微信       
      }
      state.hasBind = true
      setInfo(data)
      $message({
        message: '登录成功,正在跳转,请稍等',
        type: 'success',
        duration: 1000,
        onClose: () => {
          backPage()
        }
      })
    }
    getUnionid()
    return {
      ...toRefs(state),
      setInfo,
      backPage,
      getUnionid,
      wxL,
      wxB
    }
  }
}
</script>
Logo

前往低代码交流专区

更多推荐