vue中微信身份识别(openid)

发布时间 2023-06-25 10:58:31作者: jsper

最近做一个投票功能,为了防止用户恶意刷票,必须鉴别用户身份,对每个人投票次数限制。但投票是开放的,任何人都可以投,并非仅平台注册用户,因此只能使用用户最广泛的微信来识别用户,通过获取微信openid来判定用户是否已经投过票。

在vue中,需要添加一个静态html(weixinOAuth.html)来进行跳转和回跳处理。

机制: 在需要获取微信openid的页面跳转至weixinOAuth.html,weixinOAuth.html跳转微信官方鉴权页面获取code,微信鉴权页面再返回weixinOAuth.html带上code参数, weixinOAuth.html再拿code调用后端集成的微信api获取access_token及openid,将参数作为url query参数跳转至业务页面,业务页面再从url中获取参数。

 

跳转处理html页面:static_pages/weixin/weixinOAuth.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script type="application/javascript">
    /**
     * 此页面实现静默获取微信openId功能,主要用于一些需要使用微信账号作为身份识别的地方
     * 页面需要在微信浏览器环境下使用
     * 
     * 机制:
     * 在需要获取微信openid的页面跳转至本页面,本页面跳转微信官方鉴权页面获取code,微信鉴权页面再返回本页面带上code参数,
     * 本页面再拿code调用后端集成的微信api获取access_token及openid,将参数作为url query参数跳转至业务页面,业务页面再从url中获取openid等参数
     *
     * 用法示例:在其他vue created() 方法里使用。
     * created() {
     *     // 微信浏览器环境
     *     if (navigator.userAgent.toLowerCase().includes('micromessenger')) {
     *       // this.isWeixinBrowser = true
     *       if (!localStorage.weixinOpenid) {
     *         // 设置微信鉴权后跳转的页面地址, weixinOAuth.html完成鉴权后会自动跳转
     *         localStorage.wexinOAuthRedirect = location.href
     *         // 跳转到鉴权页面,鉴权页面在获取到openid后会写入到localStorage
     *         location.href = '/weixinOAuth.html'
     *       } else {
     *         alert('有openid:' + localStorage.weixinOpenid)
     *       }
     *     } else {
     *       // this.isWeixinBrowser = false
     *     }
     *   },
     * @param key
     * @returns {null|*}
     */
    function getUrlParam(key) {
      if (!location.href.includes('?')) {
        return null
      }
      // 通过 ? 分割获取后面的参数字符串
      let urlStr = location.href.split('?')[1];
      // 创建空对象存储参数
      let obj = {};
      // 再通过 & 将每一个参数单独分割出来
      let paramsArr = urlStr.split('&');
      for(let i = 0,len = paramsArr.length;i < len;i++){
        // 再通过 = 将每一个参数分割为 key:value 的形式
        let arr = paramsArr[i].split('=');
        obj[arr[0]] = arr[1];
      }
      return obj[key];
    }

    async function start() {
      const code = getUrlParam('code');
      const state = getUrlParam('state');
      if (!code) {
        // alert('跳转至微信平台去鉴权')
        const appid = 'wxd83?????????a44';
        const redirect_uri = encodeURIComponent(location.href); // 微信鉴权回调跳转到本页
        const url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appid}&redirect_uri=${redirect_uri}&response_type=code&scope=snsapi_base&state=abc#wechat_redirect`;
        console.info('去微信平台获取code,url:', url)
        // alert('跳转链接:' + url)
        window.location.href = url;
      } else {
        console.info(`接收到微信回调,code:${code}`)
        // alert(`接收到微信回调,code:${code},请求后端获取openId`)
        // localStorage.weixinOAuthCode = code;
        // localStorage.weixinOAuthState = state;
        // 查询用户openId
        const response = await fetch('https://你的后端.com/cus/WeixinController/weixinOAuth?code=' + code);
        const result = await response.json();
        // alert('查询用户OpenId结果:' + JSON.stringify(result))
        if (!result || !result.success) {
          // alert('获取用户信息失败')
          return
        }
        /* 正常情况将返回:
        {
          "success": "success",
          "data": {
            "access_token":"ACCESS_TOKEN",
            "expires_in":7200,
            "refresh_token":"REFRESH_TOKEN",
            "openid":"OPENID",
            "scope":"SCOPE",
            "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
          }
        }
        */
        const data = result.data
        console.info('查询用户openId data', data)
        // alert(`查询用户openId: ${data.openid}`)
        if (data.access_token && data.openid) {
          // localStorage.weixinOpenid = data.openid
          // localStorage.weixinAccessToken = data.access_token
          // alert('localStorage.weixinOpenid: ' + localStorage.weixinOpenid)
          // alert('localStorage.weixinAccessToken: ' + localStorage.weixinAccessToken)
          // 微信回调传回来的参数
          if (localStorage.wexinOAuthRedirect) {
            // alert(`跳转至业务页面:${localStorage.wexinOAuthRedirect}`)
            // 将openid拼接到要回调的页面的url参数中(这里为什么不放进localstorage里?为了提升伪造难度)
            let newUrl = localStorage.wexinOAuthRedirect
            if (localStorage.wexinOAuthRedirect.includes('?') && localStorage.wexinOAuthRedirect.includes('&')) {
              newUrl += '&'
            } else if (localStorage.wexinOAuthRedirect.includes('?')) {
              newUrl += '&'
            } else {
              newUrl += '?'
            }
            newUrl += 'weixinOpenid=' + data.openid + '&weixinAccessToken=' + data.access_token
            location.href = newUrl
          } else {
            // alert('无可跳转业务页面');
          }
        } else {
          // alert('查询用户openId失败');
        }
      }
    }
  </script>
</head>
<body onload="start()" style="display: flex;justify-content: center;align-items: center;font-size: 4vw;">
  请稍候...
</body>
</html>

 

webpack.base.conf.js添加文件复制配置

plugins: [
    new webpack.optimize.CommonsChunkPlugin('common.js'),
    new CopyWebpackPlugin([
      {
        from: 'aaa/bbb/xxx',
        to: 'xxx'
      },
      {
        from: 'static_pages/weixin',
        to: '' // 复制到根目录
      }
    ])
  ]

 

业务页面vue

created() {
    // 使用微信用户openid作为身份识别,防止刷票
    if (navigator.userAgent.toLowerCase().includes('micromessenger')) {
      this.isWeixinBrowser = true
      // 获取url参数中的openid,如果没有则跳转去鉴权
      const weixinOpenid = this.getUrlParam('weixinOpenid')
      const weixinAccessToken = this.getUrlParam('weixinAccessToken')
      if (!weixinOpenid) {
        // alert('无openid,跳转weixinOAuth.html')
        localStorage.wexinOAuthRedirect = location.href
        location.href = '/weixinOAuth.html'
      } else {
        this.weixinOpenId = weixinOpenid
        this.weixinAccessToken = weixinAccessToken
        // alert('有openid:' + localStorage.weixinOpenid)
      }
    } else {
      this.isWeixinBrowser = false
    }
  },