项目网址: https://gitee.com/vueGitee/vueGitee/tree/master/loginCheck前端
思路:1: 登陆: 用户输入帐号密码,向服务器验证是否正确,验证经过,服务器返回一个token(惟一标示用户身份的一个key),前端将token存储于 cookie,vuex UserToken 变量中,路由重定向到 path:'/'vue
2:权限验证: main.js 里面添加路由导航守卫ios
2.1: beforeEach 控制路由跳转:UserToken有值的话,判断 vuex/permission 里面的变量permissionList 是否有值,git
没值的话,根据token,调用接口获取用户对应权限,前端会有一份路由表(src/router/dynamic-router.js),它表示了每个路由可访问的权限 ,动态根据用户的 role 算出其对应有权限的路由,addRoute() 添加路由,并循环显示主页面左边侧边栏的标题,vuex
2.2: afterEach:经过vuex,设置主页面右边头部的面包导航, 左边标题栏的选中项axios
注意事项: 1: UserToken 存储在cookie中,前端设置cookie有效期 ((本项目未实现 :) 后台那边设置 UserToken 的有效期,每次请求接口,都带上token(即axios 封装 请求头里面 config.headers.Authorization = store.state.UserTokenapi
),根据后台返回的状态码,前端作出相应处理。如 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,生成不一样的接口请求方法