vue的hash路由微信授權(quán)方法

最近在開發(fā)公眾號網(wǎng)頁, 所以對授權(quán)進行了探索
示例代碼: klren0312/wechatVueHash (github.com)

1. 官方文檔步驟

1 第一步:用戶同意授權(quán)莫杈,獲取code

2 第二步:通過code換取網(wǎng)頁授權(quán)access_token

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

4 第四步:拉取用戶信息(需scope為 snsapi_userinfo)

5 附:檢驗授權(quán)憑證(access_token)是否有效

2. 問題

當(dāng)使用vue的hash路由時, 微信授權(quán)重定向到前端時, 會把路由放到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進行跳轉(zhuǎn)
如果想帶參, 可以直接放在路由后面或者放在state里面

帶參

注意: redirect_uristate都得使用encodeURIComponent進行編碼

當(dāng)然我們得拿code 去后臺請求openId等參數(shù)進行業(yè)務(wù)開發(fā)

路由攔截器中進行路由拼接與code獲取請求接口例子(本例子頁面參數(shù)是從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參數(shù)
    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等你業(yè)務(wù)參數(shù)
    getOpenId(queryObj.code)
      .then((res) => res.json())
      .then((res) => {
        if (res.code === 0) {
          // 解碼state參數(shù)
          const state = decodeURIComponent(queryObj.state)
          // 拼接url, 跳轉(zhuǎn)
          location.href = `${leftUrl}${rightUrlArr[1]}?openid=${res.openid}&state=${state}`
        } else {
          location.href = leftUrl + 'login'
        }
      })
      .catch(() => {
        location.href = leftUrl + 'login'
      })
  } else {
    next()
  }
})

2) 方法二

授權(quán)回調(diào)后端接口, 后端獲取微信的code重定向給前端, 前端拿url中的code參數(shù)再請求后端接口獲取openId等

流程
# 設(shè)置為后臺接口地址
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

# 最后跳轉(zhuǎn)地址
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')

//設(shè)置所有路由無限制訪問委刘,不需要跨域
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, 這里授權(quán)域名得配后臺的域名
app.get(SERVERURL + '/getCode', async function(req, res) {
  const { code } = req.query
  console.log(req.query)
  res.redirect(`${decodeURIComponent(BASEURL)}/#/codePage?code=${code}`)
})

// 發(fā)送模板消息
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")
})

// 創(chuàng)建菜單
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)
  })
}

/**
 * 發(fā)送模板消息
 */
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 ''
    })
}

示例測試公眾號

簡書不給發(fā) 也是牛 去github 的 readme中看吧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末束世,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锋谐,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侥衬,死亡現(xiàn)場離奇詭異餐禁,居然都是意外死亡血久,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門帮非,熙熙樓的掌柜王于貴愁眉苦臉地迎上來氧吐,“玉大人,你說我怎么就攤上這事末盔≈耍” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵陨舱,是天一觀的道長翠拣。 經(jīng)常有香客問我,道長游盲,這世上最難降的妖魔是什么误墓? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮益缎,結(jié)果婚禮上谜慌,老公的妹妹穿的比我還像新娘。我一直安慰自己链峭,他們只是感情好畦娄,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布又沾。 她就那樣靜靜地躺著弊仪,像睡著了一般熙卡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上励饵,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天驳癌,我揣著相機與錄音,去河邊找鬼役听。 笑死颓鲜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的典予。 我是一名探鬼主播甜滨,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瘤袖!你這毒婦竟也來了衣摩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤捂敌,失蹤者是張志新(化名)和其女友劉穎艾扮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體占婉,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡泡嘴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了逆济。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酌予。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖奖慌,靈堂內(nèi)的尸體忽然破棺而出霎终,到底是詐尸還是另有隱情,我是刑警寧澤升薯,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布莱褒,位于F島的核電站,受9級特大地震影響涎劈,放射性物質(zhì)發(fā)生泄漏广凸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一蛛枚、第九天 我趴在偏房一處隱蔽的房頂上張望谅海。 院中可真熱鬧,春花似錦蹦浦、人聲如沸扭吁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽侥袜。三九已至蝌诡,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間枫吧,已是汗流浹背浦旱。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留九杂,地道東北人颁湖。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像例隆,于是被迫代替她去往敵國和親甥捺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

推薦閱讀更多精彩內(nèi)容