需求分析:
1.移动端微信公众号项目,需要获取微信授权进行登录
2.默认打开首页,在路由进入之前进行判断,有如下三种情况
3.本项目有两种账号类型:商家、企业,需在登录页进行选择
- 未授权过,跳到微信授权登录页,然后再跳到登录页
- 有授权过,并且本地有登录的用户信息,跳到首页
- 有授权过,但本地没有登录的用户信息,跳到登录页
我遇到问题的一些相关知识点:
1.路由模式:hash
- 因为hash模式下,url是带
#
的,微信授权登录时候的 redirect_uri 这里跳转后需要处理下。
2.router.beforeEach:进入路由之前执行,一般用来做一些进入页面的限制。
- 开发过程中,这里遇到了死循环的情况。
- 是否对路由进行前置守卫的判断条件问题
3.微信授权登录
- js接口安全域名问题和授权回调页面域名的配置问题,
都不可以带http/https
,可参考上一篇 微信公众号 测试号配置
4.localStorage 和sessionStorage的问题
- 因为使用环境是微信浏览器,当关闭页面后,sessionStorage存储的数据会被清除掉,localStorage只有手动清除缓存时才会被清除。
- 我这里是每次打开首页时都判断是否授权登录过,所以存储的数据要一直保存,所以要用localStorage
- 过程中做过测试,通过微信的设置页点击清除缓存数据按钮,并不能清除掉localStorage的数据,所以测试中涉及到多账号切换的话,可以临时添加一个清除缓存按钮,正式环境再去掉就可以(
localStorage.clear()
)
router文件夹下的 index.js 代码:
import Vue from 'vue'
import Router from 'vue-router'
import store from '@/assets/store'
Vue.use(Router)
const routes =[
{
path: '/login',
name: 'login',
component: resolve => require(['@/pages/login'], resolve),
meta: {
title: '智能家居',
},
},
{
path: '/',
name: 'index',
component: resolve => require(['@/pages/index'], resolve),
meta: {
title: '智能家居',
requireAuth: true, // 规定需要权限才能进入
},
},
]
const router = new Router({
linkActiveClass: 'active',
routes
})
router.beforeEach((to, from, next) => {
var href = window.location.href;
let origin = window.location.origin;
let redirect_uri = encodeURIComponent(origin + '/index.html#/') // 授权登陆后的回调地址,需要进行encodeURIComponent处理
let appid = "" // 写自己公众号的appid
let code = to.query.code // 获取路径中的code参数,确认授权后微信会自动跳转到 redirect_uri ,参数code会拼接到地址后边,后续登录我需要这里的code传给后台,code只能用一次
let token = localStorage.getItem("token"),usertype = localStorage.getItem("usertype")
// 这里很重要,因为router.beforeEach 是每次进入路由前都会执行的,微信登录授权后跳转的路径格式如下:https://m.baidu.com/index.html?code=123&state=1#/
// hash模式下,跳转的url并不会按我们想的那样,把#拼在index.html后边,而是会加在url的参数的最后,所以这里要做一下判断,如果是从微信跳转来的,就直接带着我需要的参数code跳到登录页
if (href.includes("com/index.html?code")) { //url包括 com/?code 证明为从微信登录授权跳转回来的
window.location.replace(origin + '/index.html#/login?' + location.href.split('?')[1]);//拼接跳转
} else {
if(to.meta.requireAuth) { // 这个很重要,我尝试过直接判断路径是否是首页的方式,结果苹果手机没有问题,安卓手机全部都是死循环一直跳到授权登录页。使用to.meta.requireAuth 这样就正常了。
if(code&&!token&&!usertype){ // 有code且没有token和角色(已授权未登录)
localStorage.setItem("code",code) // 保存code,跳到登录页
next('/login');
} else if (!code&&token&&usertype) { //已登录,有token
next();
} else if(!code&&!token&&!usertype) { //未授权未登录,没有token和角色
//获取授权
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
}
} else { // 当不是首页时,直接进入路由
next();
}
}
})
export default router
2020.11.4更新
产品需求发生了一点变化,这里记录一下。
之前:项目入口只有一个,即点击公众号的一个菜单进入首页,在首页路由进入前,进行一些列的利用本地存储的数据相关的判断,然后判断要不要获取微信授权登录、要不要跳到登录页等
现在:项目入口改为两个,即公众号的菜单由之前的一个变成了两个:商家/企业,不需要登录页,同一个用户账号类型不可改变。如果已经是商家账号,再点企业的菜单想进入,就会跳到提示页,提示已是商家账号,然后再跳到商家的首页。
过程:开始是在之前的代码基础上改的,利用本地存储来存储账号类型用户id等数据,后来产品测试过程中要频繁测试两个账号,还要清除本地缓存的数据,而且中间问题频出,于是放弃了利用本地存储的方式
最终实现:每次从公众号菜单栏点进去,都进行一次微信授权登录跳到登录页,根据登录接口后台返回的结果判断,如果点击的是第一次登录时的账号类型,就直接跳到首页,如果不是,就跳到提示页。
代码
有改动的 router 文件夹下的 index.js 部分代码
注:
- 公众号商家入口url:
https://m.baidu.com/index.html#/?usertype=1
- 公众号企业入口url:
https://m.baidu.com/index.html#/?usertype=2
router.beforeEach((to, from, next) => {
var href = window.location.href;
let origin = window.location.origin;
let redirect_uri = encodeURIComponent(origin + '/index.html#/')
//redirect_uri,授权登陆后的回调地址,需要进行encodeURIComponent处理
let appid = "" // 公众号的appid
let code = to.query.code, state = to.query.state, usertypes = to.query.usertype
if(usertypes) { // 判断是按钮点进来的,记录点击类型
localStorage.setItem("usertypes",usertypes);
// console.log("是按钮点进来的,设置usertypes缓存")
}
if (href.includes("com/index.html?code")) { //url包括 com/?code 证明为从微信跳转回来的
// console.log("从微信授权跳转过来的")
window.location.replace(origin + '/index.html#/login?' + location.href.split('?')[1]);//拼接跳转
} else {
if(to.meta.requireAuth) { // 首页
// console.log("确定是首页")
if (usertypes) { // 从按钮点进来的
// console.log("从按钮点进来的");
//获取授权
window.location.href = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=" + appid + "&redirect_uri=" + redirect_uri + "&response_type=code&scope=snsapi_userinfo&state=STATE#wechat_redirect"
} else { // 不是从按钮点进来
next();
}
} else { // 其他页面
next();
}
}
})
登录页
created(){
this.code = this.$route.query.code || localStorage.getItem("code");
if(this.code) {
localStorage.setItem("code", this.code);
}
// console.log("code是" + this.code + "进入了登录页");
this.getToken();
},
methods:{
getToken() {
let types = localStorage.getItem('usertypes');
let code = this.code + '=' + types;
getToken(code).then((res) => {
if(res.data.code == 500) { // 账号类型错误
this.$store.dispatch('set_token',res.data.userId); // 获取返回的结果设置缓存
this.$router.replace({ // 跳到提示页
path: '/tips'
})
} else if(res.data.code == 0) { // 登录成功
this.$store.dispatch('set_token',res.data.user.userId); // 获取返回的结果设置缓存
this.$store.dispatch('set_usertype',res.data.user.type);
this.$router.replace({
path:'/',
})
} else {
this.$Message.error(res.data.msg);
}
})
},
},