美文网首页收藏
vue的hash路由微信授权方法

vue的hash路由微信授权方法

作者: ZZES_ZCDC | 来源:发表于2021-12-14 19:00 被阅读0次

    最近在开发公众号网页, 所以对授权进行了探索
    示例代码: klren0312/wechatVueHash (github.com)

    1. 官方文档步骤

    1 第一步:用户同意授权,获取code

    2 第二步:通过code换取网页授权access_token

    3 第三步:刷新access_token(如果需要)

    4 第四步:拉取用户信息(需scope为 snsapi_userinfo)

    5 附:检验授权凭证(access_token)是否有效

    2. 问题

    当使用vue的hash路由时, 微信授权重定向到前端时, 会把路由放到url最后, 例如

    https://open.weixin.qq.com/connect/oauth2/authorize?appid=yourappid&redirect_uri=https%3A%2F%2Fxx.xx.xx%2Fwechat&response_type=code&scope=snsapi_base&state=wechat&connect_redirect=1#wechat_redirect
    会变成
    https://xx.xx.xx/wechat/?code=091v5v000CeBWM1bGz2005y2Sd3v5v0q&state=wechat#/codePage
    
    hash路由问题

    3. 处理方法

    1) 方法一

    在路由拦截器中截取#/后的路由, 重新拼接成正确url, 并使用location.href进行跳转
    如果想带参, 可以直接放在路由后面或者放在state里面

    带参

    注意: redirect_uristate都得使用encodeURIComponent进行编码

    当然我们得拿code 去后台请求openId等参数进行业务开发

    路由拦截器中进行路由拼接与code获取请求接口例子(本例子页面参数是从state中获取)

    router.beforeEach(async (to, from, next) => {
      const href = window.location.href
      if (href.indexOf('/?code') > -1) {
        const urlArr = href.split('/?')
        const leftUrl = urlArr[0] + '/#/'
        const rightUrlArr = urlArr[1].split('#/')
        const queryObj = {}
        // 获取code和state参数
        rightUrlArr[0]
          .split('&')
          .map((item) => {
            const splitStr = item.split('=')
            return {
              key: splitStr[0],
              value: splitStr[1],
            }
          })
          .forEach((item) => {
            queryObj[item.key] = item.value
          })
        // 使用微信code请求后台接口拿openId等你业务参数
        getOpenId(queryObj.code)
          .then((res) => res.json())
          .then((res) => {
            if (res.code === 0) {
              // 解码state参数
              const state = decodeURIComponent(queryObj.state)
              // 拼接url, 跳转
              location.href = `${leftUrl}${rightUrlArr[1]}?openid=${res.openid}&state=${state}`
            } else {
              location.href = leftUrl + 'login'
            }
          })
          .catch(() => {
            location.href = leftUrl + 'login'
          })
      } else {
        next()
      }
    })
    

    2) 方法二

    授权回调后端接口, 后端获取微信的code重定向给前端, 前端拿url中的code参数再请求后端接口获取openId等

    流程
    # 设置为后台接口地址
    https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd5be0fe8e3c48877&redirect_uri=https%3A%2F%2Fxx.xx.xx%2Fapi%2FgetCode&response_type=code&scope=snsapi_base&state=wechat&connect_redirect=1#wechat_redirect
    
    # 最后跳转地址
    https://xx.xx.xx/wechat/#/codePage?code=001sMjFa1F7uhC0lncJa1jHXCs3sMjFa
    

    后端nodejs示例代码

    const got = require('got')
    const express = require('express')
    const bodyParser = require('body-parser')
    const ioredis = require('ioredis')
    const redis = new ioredis()
    const app = express()
    app.use('/static', express.static('public'))
    app.use(bodyParser.json())
    app.use(bodyParser.urlencoded({ extended: false }))
    
    const appID = ''
    const appsecret = ''
    
    const BASEURL = encodeURIComponent('https://xx.xx.xx/wechat')
    
    const BASEURL2 = encodeURIComponent('https://xx.xx.xx/api/getCode')
    
    //设置所有路由无限制访问,不需要跨域
    app.all('*', function(req, res, next) {
      res.header('Access-Control-Allow-Origin', '*')
      res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
      res.header('Access-Control-Allow-Methods', '*')
      next()
    })
    
    const SERVERURL = '/api'
    // 微信域名校验
    app.get(SERVERURL + '/wechat', function(req, res) {
      const { signature, timestamp, nonce, echostr } = req.query
      console.log(req.query)
      const token = 'zzes'
      jsSHA = require('jssha')
      const arr = [token, timestamp, nonce].sort()
      shaObj = new jsSHA(arr.join(''), 'TEXT')
      const currentSign = shaObj.getHash('SHA-1', 'HEX')
      if (currentSign === signature) {
        res.send(echostr)
      } else {
        res.send('')
      }
    })
    
    // 获取用户openId
    app.post(SERVERURL + '/getOpenId', function(req, res) {
      const { code } = req.body
      const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${appID}&secret=${appsecret}&code=${code}&grant_type=authorization_code`
      got(url).then(data => {
        const result = JSON.parse(data.body)
        if (result?.openid) {
          console.log('openid:' + result.openid)
          res.send({
            code: 0,
            binding: true,
            openid: result.openid
          })
        } else {
          console.log('err', result)
          res.send({
            code: result.errcode,
            binding: false,
            openid: '',
            msg: result.errmsg
          })
        }
      }).catch(err => {
        res.send({
          code: -1,
          binding: false,
          openid: '',
          msg: err.message
        })
      })
    })
    
    // 后端拿code, 这里授权域名得配后台的域名
    app.get(SERVERURL + '/getCode', async function(req, res) {
      const { code } = req.query
      console.log(req.query)
      res.redirect(`${decodeURIComponent(BASEURL)}/#/codePage?code=${code}`)
    })
    
    // 发送模板消息
    app.get(SERVERURL + '/sendMsg', async function(req, res) {
      const { openid } = req.query
      const result = await sendTemplateMsg(openid)
      res.send(result)
    })
    
    //端口:18888
    var server = app.listen(28848, function() {
      console.log("127.0.0.1:28848")
    })
    
    // 创建菜单
    setWechatMenu()
    async function setWechatMenu() {
      const url = encodeURIComponent(`/#/`)
      const menu = {
        button: [
          {
            name: '菜单',
            sub_button: [
              { 
                type:'view',
                name:'测试一',
                url:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${BASEURL}${encodeURIComponent(`/#/`)}wechat&response_type=code&scope=snsapi_base&state=111#wechat_redirect`
              },
              { 
                type:'view',
                name:'测试二',
                url:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${BASEURL}${encodeURIComponent(`/#/`)}wechat2&response_type=code&scope=snsapi_base&state=111#wechat_redirect`
              },
              { 
                type:'view',
                name:'测试',
                url:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${BASEURL2}&response_type=code&scope=snsapi_base&state=111#wechat_redirect`
              }
            ]
          }
        ]
      }
      let accessToken = await redis.get('access_token')
      if (!accessToken) {
        accessToken = await getAccessToken()
      }
      got({
        url: `https://api.weixin.qq.com/cgi-bin/menu/create?access_token=${accessToken}`,
        method: 'POST',
        body: JSON.stringify(menu)
      }).then(data => {
        const result = JSON.parse(data.body)
        console.log('菜单', result)
      })
    }
    
    /**
     * 发送模板消息
     */
    async function sendTemplateMsg(openid) {
      let accessToken = await redis.get('access_token')
      if (!accessToken) {
        accessToken = await getAccessToken()
      }
      const state = encodeURIComponent(`wechat&id=${Math.floor(Math.random() * 100)}`)
      return got({
        url: `https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=${accessToken}`,
        method: 'POST',
        body: JSON.stringify({
          touser: openid,
          template_id: 'WfcomWPkkbQlvTJXJpzFVWGc14hOeyI23TXgHPST8-I',
          url: `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appID}&redirect_uri=${BASEURL}${encodeURIComponent(`/#/`)}wechat1&response_type=code&scope=snsapi_base&state=${state}#wechat_redirect`,
          data: {
            time: {
              value: new Date().toLocaleString(),
              color: '#323232'
            },
            content: {
              value: '您有新的消息, 请点击查看',
              color: '#ff0000'
            }
          }
        })
      }).then(data => {
        const result = JSON.parse(data.body)
        return result
      })
    }
    
    /**
     * 获取access_token
     */
    function getAccessToken() {
      return got(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appsecret}`)
        .then(data => {
          console.log(data.body)
          const result = JSON.parse(data.body)
          if (result?.access_token) {
            redis.set('access_token', result.access_token, 'EX', result.expires_in - 60)
            return result.access_token
          } else {
            console.log('err', result)
            return ''
          }
        })
        .catch(err => {
          console.log(err)
          return ''
        })
    }
    

    示例测试公众号

    简书不给发 也是牛 去github 的 readme中看吧

    相关文章

      网友评论

        本文标题:vue的hash路由微信授权方法

        本文链接:https://www.haomeiwen.com/subject/kodyfrtx.html