前端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
所以首次打開瀏覽器可能會有登陸已過時的提示,這裏相信你們都能找到適合本身的處理方案
通過這些簡單又好用的處理,一個基本知足需求的先後端分離前端鑑權方案就誕生啦