前端Vue全家桶,後臺.net。css
對於需求一、二、3,採用異步加載路由方案
將在vuex裏處理好的路由數據經過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" }); } } });
##### 注意vue
我這裏無需鑑權的路由直接寫在router文件夾下的index.js,經過路由元信息meta攜帶指定標識
{ path: "/err-404", name: "err404", meta: { authentication: false }, component: resolve => require(["../views/error/404.vue"], resolve) },
上面說到路由是根據後臺返回菜單數據根據必定規則生成,所以一些不是菜單,又須要登陸狀態的路由,我寫在router文件夾下的router.js裏,在上面步驟4裏處理後臺返回菜單數據時,和處理好的菜單路由數據合併一同經過addRoutes
推入。
這樣作會有必定的被地址欄入侵的風險,可是筆者這裏大可能是不過重要的路由,若是你要求咳咳,能夠定一份字典來和後臺接口配合精確加載每個路由。
// 加入企業 { path: "/join-company", name: "join-company", component: resolve => require([`@/views/index/join-company.vue`], resolve) },
在vuex中將分配的菜單數據轉化爲前端可用的路由數據,我是這樣作的:
管理系統在新增菜單時須要填寫一個頁面地址字段Url
,前端獲得後臺菜單數據後根據Url
字段來匹配路由加載的文件路徑,每一個菜單一個文件夾的好處是:你能夠在這裏拆分js、css和此菜單私有組件等
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個。
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改造後的全局路由守衛是這樣的:ios
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
,而且嘗試百度其餘屬性無效,所以在請求發出前作了粗暴處理:
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
所以首次打開瀏覽器可能會有登陸已過時的提示,這裏相信你們都能找到適合本身的處理方案
通過這些簡單又好用的處理,一個基本知足需求的先後端分離前端鑑權方案就誕生啦vuex