前端Vue全家桶,后台.net。css
对于需求一、二、3,采用异步加载路由方案前端
addRoutes
异步推入路由router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取权限菜单
if (store.state.sidebar.userRouter.length === 0) {
// 无菜单时拉取
getMenuRouter()
.then(res => {
let _menu = res.data.Data.ColumnDataList || [];
// if (res.data.Data.ColumnDataList.length > 0) {
// 整理菜单&路由数据
store.commit("setMenuRouter", _menu);
// 推入权限路由列表
router.addRoutes(store.state.sidebar.userRouter);
next({...to, replace: true });
// }
})
.catch(err => {
// console.log(err);
// Message.error("服务器链接失败");
});
} else {
//当有用户权限的时候,说明全部可访问路由已生成 如访问没权限的菜单会自动进入404页面
if (to.path == "/login") {
next({
name: "index"
});
} else {
next();
}
}
} else {
// 无登陆状态时重定向至登陆 或可进入无需登陆状态路径
if (to.path == "/login" || to.meta.auth === 0) {
next();
} else {
next({
path: "/login"
});
}
}
});
复制代码
我这里无需鉴权的路由直接写在router文件夹下的index.js,经过路由元信息meta携带指定标识vue
{
path: "/err-404",
name: "err404",
meta: {
authentication: false
},
component: resolve => require(["../views/error/404.vue"], resolve)
},
复制代码
上面说到路由是根据后台返回菜单数据根据必定规则生成,所以一些不是菜单,又须要登陆状态的路由,我写在router文件夹下的router.js里,在上面步骤4里处理后台返回菜单数据时,和处理好的菜单路由数据合并一同经过
addRoutes
推入。 这样作会有必定的被地址栏入侵的风险,可是笔者这里大可能是不过重要的路由,若是你要求咳咳,能够定一份字典来和后台接口配合精确加载每个路由。ios
// 加入企业
{
path: "/join-company",
name: "join-company",
component: resolve => require([`@/views/index/join-company.vue`], resolve)
},
复制代码
在vuex中将分配的菜单数据转化为前端可用的路由数据,我是这样作的: 管理系统在新增菜单时须要填写一个页面地址字段
Url
,前端获得后台菜单数据后根据Url
字段来匹配路由加载的文件路径,每一个菜单一个文件夹的好处是:你能够在这里拆分js、css和此菜单私有组件等vuex
menu.forEach(item => {
let routerItem = {
path: item.Url,
name: item.Id,
meta: {
auth: item.Children,
}, // 路由元信息 定义路由时便可携带的参数,可用来管理每一个路由的按钮操做权限
component: resolve =>
require([`@/views${item.Url}/index.vue`], resolve) // 路由映射真实视图路径
};
routerBox.push(routerItem);
});
复制代码
关于如何精确控制每个按钮我是这样作的,将按钮编码放在路由元信息里,在当前路由下匹配来控制页面上的按钮是否建立。 菜单数据返回的都是多级结构,每一个菜单下的子集就是当前菜单下的按钮权限码数组,我把每一个菜单下的按钮放在此菜单的路由元信息
meta.auth
中。这样做的好处是:按钮权限校验只需匹配每一个菜单路由元信息下的数据,这样校验池长度一般不会超过5个。axios
created() {
this.owner = this.$route.meta.auth.map(item => item.Code);
}
methods: {
matchingOwner(auth) {
return this.owner.some(item => item === auth);
}
}
复制代码
需求4自动更新token,就是简单的时间判断,并在请求头添加字段来通知后台更新token并在头部返回,前端接受到带token的请求就直接更新token后端
// 在axios的请求拦截器中
let token = getSession(auth_code);
if (token) config.headers.auth = token;
if (tokenIsExpire(token)) {
// 判断是否须要刷新jwt
config.headers.refreshtoken = true;
}
// 在axios的响应拦截器中
if (res.headers.auth) {
setSession(auth_code, res.headers.auth);
}
复制代码
对于需求5的处理比较麻烦,要跨tab页只能经过
cookie
或local
,笔者这里不容许使用cookie
所以采用的localstorage
。经过打开的新页面读取localstorage
内的token
数据来同步多个页面的帐号信息。token
使用的jwt
并前端md5加密。 这里须要注意一点是页面切换要当即同步帐号信息。数组
通过需求5改造后的全局路由守卫是这样的:浏览器
function _AUTH_() {
// 切换窗口时校验帐号是否发生变化
window.addEventListener("visibilitychange", function() {
let Local_auth = getLocal(auth_code, true);
let Session_auth = getSession(auth_code);
if (document.hidden == false && Local_auth && Local_auth != Session_auth) {
setSession(auth_code, Local_auth, true);
router.go(0)
}
})
router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取权限菜单
if (store.state.sidebar.userRouter.length === 0) {
// 无菜单时拉取
getMenuRouter()
.then(res => {
let _menu = res.data.Data.ColumnDataList || [];
// if (res.data.Data.ColumnDataList.length > 0) {
// 整理菜单&路由数据
store.commit("setMenuRouter", _menu);
// 推入权限路由列表
router.addRoutes(store.state.sidebar.userRouter);
next({...to, replace: true });
// }
})
.catch(err => {
// console.log(err);
// Message.error("服务器链接失败");
});
} else {
//当有用户权限的时候,说明全部可访问路由已生成 如访问没权限的菜单会自动进入404页面
if (to.path == "/login") {
next({
name: "index"
});
} else {
next();
}
}
} else {
// 无登陆状态时重定向至登陆 或可进入无需登陆状态路径
if (to.path == "/login" || to.meta.auth === 0) {
next();
} else {
next({
path: "/login"
});
}
}
});
}
复制代码
通过需求5改造后的axios的请求拦截器是这样的,由于ie没法使用
visibilitychange
,而且尝试百度其余属性无效,所以在请求发出前作了粗暴处理:bash
if (ie浏览器) {
setLocal('_ie', Math.random())
let Local_auth = getLocal(auth_code, true);
let Session_auth = getSession(auth_code);
if (Local_auth && Local_auth != Session_auth) {
setSession(auth_code, Local_auth, true);
router.go(0)
return false
}
}
复制代码
这里有一个小问题须要注意:由于用的
local
所以首次打开浏览器可能会有登陆已过时的提示,这里相信你们都能找到适合本身的处理方案
通过这些简单又好用的处理,一个基本知足需求的先后端分离前端鉴权方案就诞生啦