項目網址: https://gitee.com/vueGitee/vueGitee/tree/master/loginCheck
思路:1: 登錄: 用戶輸入賬號密碼,向服務器驗證是否正確,驗證通過,服務器返回一個token(唯一標示用戶身份的一個key),前端將token存儲於 cookie,vuex UserToken 變量中,路由重定向到 path:'/'
2:權限驗證: main.js 里面添加路由導航守衛
2.1: beforeEach 控制路由跳轉:UserToken有值的話,判斷 vuex/permission 里面的變量permissionList 是否有值,
沒值的話,根據token,調用接口獲取用戶對應權限,前端會有一份路由表(src/router/dynamic-router.js),它表示了每一個路由可訪問的權限 ,動態根據用戶的 role 算出其對應有權限的路由,addRoute() 添加路由,並循環顯示主頁面左邊側邊欄的標題,
2.2: afterEach:通過vuex,設置主頁面右邊頭部的面包導航, 左邊標題欄的選中項
注意事項: 1: UserToken 存儲在cookie中,前端設置cookie有效期 ((本項目未實現 :) 后台那邊設置 UserToken 的有效期,每次請求接口,都帶上token(即axios 封裝 請求頭里面 config.headers.Authorization = store.state.UserToken
),根據后台返回的狀態碼,前端做出相應處理。如 res.code === 10001 ,表示token失效,toast 提醒,再退出登錄; res.code === 10002,表示網絡異常,前端toast 提醒; 若沒有相應權限.....)
步驟:
1:vuex 聲明變量 (文件路徑:src/store/state.js)
import {setCookie, getCookie} from '../utils/cookie' export default { get UserToken () { // 判斷用戶是否登錄 // return localStorage.getItem('token') // return cookies.get('token') return getCookie('token') }, set UserToken (value) { setCookie('token', value, 10) console.log('getCookie(token)', getCookie('token')) // return localStorage.setItem('token', value) // return cookies.set('token', value) }, // 導航菜單是否折疊 isSidebarNavCollapse: false, // 面包屑導航列表 crumbList: [] }
2: 登錄頁面,點擊‘登錄’按鈕,調用 login 函數,調用后台接口,前端把接口返回數據中的token變量放到 vuex UserToken,並且放在cookie 里面(防止用戶刷新瀏覽器頁面,vuex 里面的state恢復初始值 ),設置路由重定向到 ‘/’
async login () { // 此時為異步函數 try { let data = await login() // await 同步 等待 需要的等到 await 后面的函數執行完以后有了返回結果,才能執行下面的代碼 let token = data.token this.$store.commit('LOGIN_IN', token) this.$router.replace('/') } catch (e) { console.log(e) } }
LOGIN_IN (state, token) { // 這步操作,實現了state 中的 UserToken 賦值, 同時執行 set UserToken (value) 方法,把token 值放到了 sessionStorage(或cookie) 里面 // 因為刷新頁面,vuex 變量 中的值 變成默認值 '' ,所以放到本地緩存里面,刷新頁面以后,調用 get UserToken () 方法 從本地緩存中獲取token取值 state.UserToken = token },
get UserToken () { // 判斷用戶是否登錄 // return localStorage.getItem('token') return getCookie('token') }, set UserToken (value) { setCookie('token', value, 10) // cookie 有效期設置為 10s console.log('getCookie(token)', getCookie('token')) // return localStorage.setItem('token', value) },
3:main.js 添加 路由鈎子函數
router.beforeEach((to, from, next) => { if (!store.state.UserToken) { // some 方法 判斷 即將被打開的路由里面,是否有 requiresAuth 屬性 ,以此來判斷是否驗證,但凡有一個取值為true,則去登錄頁面,去登錄驗證 if (to.matched.length > 0 && !to.matched.some(record => { return record.meta.requiresAuth })) { next() } else { next({path: '/login'}) } } else { if (!store.state.permission.permissionList) { // (!null === true) 取值為空,說明路由還沒有動態創建 store.dispatch('permission/FETCH_PERMISSION').then(() => { next({path: to.path}) }) } else { if (to.path !== '/login') { next() } else { next(from.fullPath) } } } })
沒有UserToken , 不需要驗證的話,直接 next(), 需要驗證的話,跳登錄頁面
有UserToken(已登錄),permissionList 不存在 ,調用 actions 里面的 FETCH_PERMISSION 方法,去生成動態路由。
4: FETCH_PERMISSION 方法
state: { // 所有路由 permissionList: null, // 導航菜單 slidebarMenu: [], // 當前 active 導航菜單 currentMenu: '' },
async FETCH_PERMISSION ({commit, state}) { let permissionList = await fetchPermission() let routes = recursionRouter(permissionList, dynamicRouter) console.log('routes', routes) let mainContainer = DynamicRoutes.find(v => v.path === '') let children = mainContainer.children children.push(...routes) commit('SET_MENU', children) // 生成側邊欄 setDefaultRoute([mainContainer]) // 初始化路由 let initialRoutes = router.options.routes router.addRoutes(DynamicRoutes) // 生成完成路由表 commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes]) //permissionList變量 表示完整的路由,在main.js router.beforeEach鈎子函數中,調用了它
}
5: 路由 router/index.js: 路由一開始只有 /login
// 初始化路由 export default new Router({ routes: [ { path: '/login', component: Login } ] })
// 准備動態添加路由 export const DynamicRoutes = [ { path: '', component: Layout, name: 'container', redirect: 'home', meta: { requiresAuth: true, name: '首頁' }, children: [ { path: 'home', component: Home, name: 'home', meta: { name: '首頁', icon: 'icon-home' } } ] }, { path: '/403', component: Forbidden }, { path: '*', component: NotFound } ]
登錄以后,頁面重定向到 path: '/' 即 path: '', 此時又重定向到子路由 path: 'home', 因為是子路由,所以頁面顯示是 父路由 (父組件:Layout)+ 子路由(子組件:Home),最終子組件在
<router-view class="content"></router-view>
axios 封裝在 src/config/httpConfig.js 中 src/api/permission.js中根據不同的接口url,生成不同的接口請求方法