vue微信受權解決方案[如何本地實現受權]

github:vue-wechat-auth

以前是本身太年輕,寫什麼【最終解決方案】。這一次的項目vue移動端電商項目,作了不少的優化。你們都知道受權須要每次都要發佈到線上,本地的須要代理,這讓咱們很頭疼。後面會介紹一個本地直接受權的方式,真的超級香。html

時隔幾年,第三次升級個人微信受權,每一次思路都更加清晰,當個人知識愈來愈廣,愈來愈深,我相信會有第四次,第五次。。。前端

另外也優化:vue

  • 微信分享
  • keep-alive返回到上次瀏覽的位置
  • vue-router 全部頁面攜帶參數
  • ...

後續會持續分享,接下來首先優化的就是受權邏輯的優化。webpack

場景

整個項目不管什麼頁面進入都須要進行受權,通常微信公衆號H5項目這一點都是須要作到ios

接下來咱們開始吧,先克隆安裝依賴,不要着急啓動,先把準備工做作好。git

// 克隆項目
git clone https://github.com/sunnie1992/vue-wechat-auth.git
//安裝依賴
npm install 複製代碼

實現本地微信受權

1.工具

實現本地開發受權,你須要使用微信開發者工具,網頁是沒有辦法直接本地拿到受權的。github

2.將auth.html部署到服務器上

這裏咱們用到了 GetWeixinCode ,使用的時候修復了一些bugweb

  • 攜帶的參數在受權完以後沒能所有帶回來。
  • hash回調url錯誤問題

部署auth.html(在github項目的根目錄下)到你的微信受權回調域名的目錄下。vue-router

  1. 前往微信公衆平臺->接口權限->網頁受權獲取用戶基本信息->修改,填寫受權回調頁面域名,例如www.abc.com
  2. www.abc.com域名下部署auth.html,不必定是根目錄,例如:https://www.abc.com/xxx/auth....
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>微信登陸</title>
  </head>

  <body>
    <script>
      var GWC = {
        version: '1.2.0',
        urlParams: {},
        appendParams: function (url, params) {
                if (params) {
                    var baseWithSearch = url.split('#')[0];
                    var hash = window.location.hash.split('#')[1];
                    if (hash) {
                      baseWithSearch = baseWithSearch + '#' + hash;
                    } 
                    for (var key in params) {
                        var attrValue = params[key];
                        if (attrValue !== undefined) {
                            var newParam = key + "=" + attrValue;
                            if (baseWithSearch.indexOf('?') > 0) {
                                var oldParamReg = new RegExp('^' + key + '=[-%.!~*\'\(\)\\w]*', 'g');
                                if (oldParamReg.test(baseWithSearch)) {
                                    baseWithSearch = baseWithSearch.replace(oldParamReg, newParam);
                                } else {
                                    baseWithSearch += "&" + newParam;
                                }
                            } else {
                                baseWithSearch += "?" + newParam;
                            }
                        }
                    }
                }
               
                return baseWithSearch;
            
            },
        getUrlParams: function() {
          var pairs = location.search.substring(1).split('&')
          for (var i = 0; i < pairs.length; i++) {
            var pos = pairs[i].indexOf('=')
            if (pos === -1) {
              continue
            }
            GWC.urlParams[pairs[i].substring(0, pos)] = decodeURIComponent(pairs[i].substring(pos + 1))
          }
        },
        doRedirect: function() {
          var code = GWC.urlParams['code']
          var appId = GWC.urlParams['appid']
          var scope = GWC.urlParams['scope'] || 'snsapi_base'
          var state = GWC.urlParams['state']
          var isMp = GWC.urlParams['isMp'] //isMp爲true時使用開放平臺做受權登陸,false爲網頁掃碼登陸
          var baseUrl
          var redirectUri

          if (!code) {
            baseUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize#wechat_redirect'
            if (scope == 'snsapi_login' && !isMp) {
              baseUrl = 'https://open.weixin.qq.com/connect/qrconnect'
            }

            //第一步,沒有拿到code,跳轉至微信受權頁面獲取code
            redirectUri = GWC.appendParams(baseUrl, {
              appid: appId,
              redirect_uri: encodeURIComponent(location.href),
              response_type: 'code',
              scope: scope,
              state: encodeURIComponent(state)
            })
        
          } else {
            const params = Object.assign({}, GWC.urlParams)
            delete params.backUrl
            //第二步,從微信受權頁面跳轉回來,已經獲取到了code,再次跳轉到實際所需頁面
            redirectUri = GWC.appendParams(GWC.urlParams['backUrl'], params)
          }

          location.href = redirectUri
        }
      }

      GWC.getUrlParams()
      GWC.doRedirect()
    </script>
  </body>
</html>

3.實現代碼邏輯

主要文件:src/plugins/wechatAuth.jsvuex

微信受權相關方法封裝這裏引用的是[vue-wechat-login],作了簡單的修改,直接在路由鉤子文件permission.js使用。

const qs = require('qs')
// 應用受權做用域,snsapi_base (不彈出受權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出受權頁面,可經過openid拿到暱稱、性別、所在地。而且,即便在未關注的狀況下,只要用戶受權,也能獲取其信息)
const SCOPES = ['snsapi_base', 'snsapi_userinfo']

class VueWechatAuthPlugin {
  constructor() {
    this.appid = null
    this.redirect_uri = null
    this.scope = SCOPES[1]
    this._code = null
    this._redirect_uri = null
  }

  static makeState() {
    return (
      Math.random()
        .toString(36)
        .substring(2, 15) +
      Math.random()
        .toString(36)
        .substring(2, 15)
    )
  }
  setAppId(appid) {
    this.appid = appid
  }
  set redirect_uri(redirect_uri) {
    this._redirect_uri = encodeURIComponent(redirect_uri)
  }
  get redirect_uri() {
    return this._redirect_uri
  }
  get state() {
    return localStorage.getItem('wechat_auth:state')
  }
  set state(state) {
    localStorage.setItem('wechat_auth:state', state)
  }
  get authUrl() {
    if (this.appid === null) {
      throw new Error('appid must not be null')
    }
    if (this.redirect_uri === null) {
      throw new Error('redirect uri must not be null')
    }
    this.state = VueWechatAuthPlugin.makeState()
    return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this.appid}&redirect_uri=${this.redirect_uri}&response_type=code&scope=${this.scope}&state=${this.state}#wechat_redirect`
  }
  returnFromWechat(redirect_uri) {
    const parsedUrl = qs.parse(redirect_uri.split('?')[1])

    if (process.env.NODE_ENV === 'development') {
      this.state = null
      this._code = parsedUrl.code
    } else {
      if (this.state === null) {
        throw new Error("You did't set state")
      }
      if (parsedUrl.state === this.state) {
        this.state = null
        this._code = parsedUrl.code
      } else {
        this.state = null
        throw new Error(`Wrong state: ${parsedUrl.state}`)
      }
    }
  }
  get code() {
    if (this._code === null) {
      throw new Error('Not get the code from wechat server!')
    }
    const code = this._code
    this._code = null
    // console.log('code: ' + code)
    return code
  }
}
const vueWechatAuthPlugin = new VueWechatAuthPlugin()

export default vueWechatAuthPlugin

4.設置相關變量

在開發以前你要首先在下面三個文件設置兩個變量,若是你已經啓動項目,設置後須要重啓。

.env.development 

.env.staging 

 .env.production

VUE_APP_WECHAT_APPID是你的appid,在.env.[環境] 文件中設置

VUE_APP_WECHAT_AUTH_URL是你的auth.html 訪問地址。在.env.[環境] 文件中設置

5.permission.js 路由守衛

設置受權白名單whiteList,受權失敗,或者其餘錯誤進入404頁面。

// 設置回調地址,本地和線上不一樣 
wechatAuth.redirect_uri = processUrl()  
await store.dispatch('user/setLoginStatus', 1)
// 跳轉完整的受權地址
window.location.href = wechatAuth.authUrl複製代碼

wechatAuth.authUrl 地址 https://open.weixin.qq.com/co..._uri 設置調用 processUrl方法,本地開發,回調設置本地路徑會報redirect_uri錯誤,因此咱們跳到中間頁auth.html再攜帶code跳會到backUrl。

本地環境返回受權的回調地址:

`${process.env.VUE_APP_WECHAT_AUTH_URL}?backUrl=${window.location.href}`複製代碼

 其中 process.env.VUE_APP_WECHAT_AUTH_URL 就是中間受權頁面的網址。backUrl後面跟的是你本地開發的地址。https://www.abc.com/auth.html...://localhost:9018/#/

線上環境返回的是正常的微信受權地址:

https://open.weixin.qq.com/co...

redirect_uri是線上的地址,不用中間頁跳轉。

import qs from 'qs'
import router from '@/router'
import store from '@/store'
import wechatAuth from '@/plugins/wechatAuth'
// 設置APPID
wechatAuth.setAppId(process.env.VUE_APP_WECHAT_APPID)
const whiteList = ['/404']
router.beforeEach(async (to, from, next) => {
  // 在白名單,直接進入
  if (whiteList.indexOf(to.path) !== -1) {
    return next()
  }
  const {loginStatus} = store.getters
  switch (Number(loginStatus)) {
    case 0:
      // 獲取跳轉地址
      wechatAuth.redirect_uri = processUrl()
      await store.dispatch('user/setLoginStatus', 1)
      window.location.href = wechatAuth.authUrl
      break
    case 1:
      try {
        wechatAuth.returnFromWechat(to.fullPath)
        const code = wechatAuth.code
        console.log('code==', code)
        // 經過code換取token
        // await store.dispatch('user/loginWechatAuth', code)
        await store.dispatch('user/setLoginStatus', 2)
        next()
      } catch (err) {
        await store.dispatch('user/setLoginStatus', 0)
        next('/404')
      }
      break
    case 2:
      next()
      break
    default:
      break
  }
})

/**
 * 處理url連接
 * @returns {string}
 */
function processUrl() {
  // 本地環境換經過auth.html拿code
  if (process.env.NODE_ENV === 'development') {
    // 中間受權頁地址
    return `${process.env.VUE_APP_WECHAT_AUTH_URL}?backUrl=${window.location.href}`
  }
  const url = window.location.href
  // 解決屢次登陸url添加劇復的code與state問題
  const urlParams = qs.parse(url.split('?')[1])
  let redirectUrl = url
  if (urlParams.code && urlParams.state) {
    delete urlParams.code
    delete urlParams.state
    const query = qs.stringify(urlParams)
    if (query.length) {
      redirectUrl = `${url.split('?')[0]}?${query}`
    } else {
      redirectUrl = `${url.split('?')[0]}`
    }
  }
  return redirectUrl
}

當配置好參數,本地啓動後,能夠正常進入受權頁面

// 啓動
npm run serve 複製代碼

WX20200223-114848@2x.png

贊成以後就看到code值了
WX20200223-145454@2x.png

到此主要的流程就結束了。當受權成功後須要經過code換取token,由於並無對接後臺,因此這裏我註釋掉了

WX20200223-150114@2x.png

接下來操做在vuex中進行,用戶根據需求對接後臺接口便可。

/src/store/modules/user.js

import {loginByCode} from '@/api/user'
import {
  saveToken,
  saveLoginStatus,
  saveUserInfo,
  removeToken,
  removeUserInfo,
  removeLoginStatus,
  loadLoginStatus,
  loadToken,
  loadUserInfo
} from '@/utils/cache'
const state = {
  loginStatus: loadLoginStatus(), // 登陸狀態
  token: loadToken(), // token
  userInfo: loadUserInfo() // 用戶登陸信息
}

const mutations = {
  SET_USERINFO: (state, userInfo) => {
    state.userInfo = userInfo
  },
  SET_LOGIN_STATUS: (state, loginStatus) => {
    state.loginStatus = loginStatus
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  }
}

const actions = {
  // 登陸相關,經過code獲取token和用戶信息,用戶根據本身的需求對接後臺
  loginWechatAuth({commit}, code) {
    const data = {
      code: code
    }
    return new Promise((resolve, reject) => {
      loginByCode(data)
        .then(res => {
          // 存用戶信息,token
          commit('SET_USERINFO', saveUserInfo(res.data.user))
          commit('SET_TOKEN', saveToken(res.data.token))
          resolve(res)
        })
        .catch(error => {
          reject(error)
        })
    })
  },
  // 設置狀態
  setLoginStatus({commit}, query) {
    if (query === 0 || query === 1) {
      // 上線打開註釋,本地調試註釋掉,保持信息最新
      removeToken()
      removeUserInfo()
    }
    // 設置不一樣的登陸狀態
    commit('SET_LOGIN_STATUS', saveLoginStatus(query))
  },
  // 登出
  fedLogOut() {
    // 刪除token,用戶信息,登錄狀態
    removeToken()
    removeUserInfo()
    removeLoginStatus()
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

utils/cache.js文件用戶來緩存數據

import cookies from 'js-cookie'
import storage from 'good-storage'
const LoginStatusKey = 'Login-Status' // 登陸態 0未受權未登陸 1受權未登陸 2 登錄成功
const TokenKey = 'Access-Token' // token
const UserInfoKey = 'User-Info' // 用戶信息 {} {...}
// 獲取登陸狀態
export function loadLoginStatus() {
  return cookies.get(LoginStatusKey) || 0
}
// 保持登陸狀態
export function saveLoginStatus(status) {
  cookies.set(LoginStatusKey, status, {expires: 7})
  return status
}
// 刪除登陸狀態
export function removeLoginStatus() {
  cookies.remove(LoginStatusKey)
  return ''
}
// 獲取token
export function loadToken() {
  return storage.get(TokenKey, '')
}
// 保存token
export function saveToken(token) {
  storage.set(TokenKey, token)
  return token
}
// 刪除token
export function removeToken() {
  storage.remove(TokenKey)
  return ''
}
// 獲取用戶信息
export function loadUserInfo() {
  return storage.get(UserInfoKey, {})
}
// 保存用戶信息
export function saveUserInfo(userInfo) {
  storage.set(UserInfoKey, userInfo)
  return userInfo
}
// 刪除用戶信息
export function removeUserInfo() {
  storage.remove(UserInfoKey)
  return {}
}

項目地址

github:vue-wechat-auth

另外,項目架構介紹請看[ vue-h5-template]基於vue-cli4.0+webpack 4+vant ui + sass+ rem適配方案+axios封裝。若是你只須要受權邏輯,只要把涉及到的相關文件放到你的項目下就能夠。

關於我

若是您遇到了問題能夠給我提 issues 

您也能夠掃描添加下方的微信並備註 Sol 加前端交流羣,交流學習。

若是對你有幫助送我一顆小星星,你的star是我前進的動力(づ ̄3 ̄)づ╭❤~

相關文章
相關標籤/搜索