以前是本身太年轻,写什么【最终解决方案】。这一次的项目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 ̄)づ╭❤~