以前是本身太年輕,寫什麼【最終解決方案】。這一次的項目vue移動端電商項目,作了不少的優化。你們都知道受權須要每次都要發佈到線上,本地的須要代理,這讓咱們很頭疼。後面會介紹一個本地直接受權的方式,真的超級香。html
時隔幾年,第三次升級個人微信受權,每一次思路都更加清晰,當個人知識愈來愈廣,愈來愈深,我相信會有第四次,第五次。。。前端
另外也優化:vue
後續會持續分享,接下來首先優化的就是受權邏輯的優化。webpack
整個項目不管什麼頁面進入都須要進行受權,通常微信公衆號H5項目這一點都是須要作到ios
接下來咱們開始吧,先克隆安裝依賴,不要着急啓動,先把準備工做作好。git
// 克隆項目 git clone https://github.com/sunnie1992/vue-wechat-auth.git //安裝依賴 npm install 複製代碼
實現本地開發受權,你須要使用微信開發者工具,網頁是沒有辦法直接本地拿到受權的。github
這裏咱們用到了 GetWeixinCode ,使用的時候修復了一些bugweb
部署auth.html
(在github項目的根目錄下)到你的微信受權回調域名的目錄下。vue-router
www.abc.com
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>
主要文件: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
在開發以前你要首先在下面三個文件設置兩個變量,若是你已經啓動項目,設置後須要重啓。
.env.development
.env.staging
.env.production
VUE_APP_WECHAT_APPID是你的appid,在.env.[環境] 文件中設置
VUE_APP_WECHAT_AUTH_URL是你的auth.html 訪問地址。在.env.[環境] 文件中設置
設置受權白名單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 複製代碼
贊成以後就看到code值了
到此主要的流程就結束了。當受權成功後須要經過code換取token,由於並無對接後臺,因此這裏我註釋掉了
接下來操做在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 {} }
另外,項目架構介紹請看[ vue-h5-template]基於vue-cli4.0+webpack 4+vant ui + sass+ rem適配方案+axios封裝。若是你只須要受權邏輯,只要把涉及到的相關文件放到你的項目下就能夠。
若是您遇到了問題能夠給我提 issues
您也能夠掃描添加下方的微信並備註 Sol 加前端交流羣,交流學習。
若是對你有幫助送我一顆小星星,你的star是我前進的動力(づ ̄3 ̄)づ╭❤~