项目模板采用vue-element-admin,使用vue和element-ui实现
1.目录结构
image.png
permission.js
登录流程中,src/permission.js是最重要的环节,这个文件是路由的全局钩子(beforeEach和afterEach),全局钩子的意思就是每次跳转的时候可以根据情况进行拦截,不让他跳转。使用场景就是有些页面需要登录才能访问,这时候就可以在beforeEach中校验用户登录状态来进行拦截。
utils/auth.js
设置token到cookie中的操作封装。
router
有关路由的一些设置
二、登录流程解析
登录页面:view/login/index.vue
image.png
点击登录按钮后,触发handleLogin方法,利用validate进行表单验证,如果验证通过,调用user/login方法传递表单里的数据,根据.then回调执行this.$router.push方法,这个方法用于跟踪:我是从哪里跳到/login页面的,登录后我就返回哪里。
/user/login方法:src/store/modules/user.js
image.png这个方法主要做了以下工作:登录验证,登录成功后,分别把token保存在vuex和cookie中。
permission.js :src/permission.js
router.beforeEach(async(to, from, next) => {
// 从cookie中取得token
const hasToken = getToken()
// 如果有token 也就是已经登陆的情况下
if (hasToken) {
// 并且要前往的路径是'/login' 则返回 '/'
if (to.path === '/login') {
next({ path: '/' })
} else {
// 从store中取得用户的 roles, 也就是用户的权限 并且用户的权限数组必须有一个以上
const hasRoles = store.getters.roles && store.getters.roles.length > 0
// 有权限则直接进入
if (hasRoles) {
next()
} else {
// 没有权限的话
try {
// 获取用户信息
const { roles } = await store.dispatch('user/getInfo')
// 生成可访问路由
const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
// 将可访问路由添加到路由上
router.addRoutes(accessRoutes)
// 进入路由
next({ ...to, replace: true })
} catch (error) {
// 如果出现异常 清空路由
await store.dispatch('user/resetToken')
// Message提示错误
Message.error(error || 'Has Error')
// 跳到login页面重新登陆
next(`/login?redirect=${to.path}`)
}
}
}
} else {
// 没有token 也就是没有登陆的情况下
// 判断是否是白名单(也就是说不需要登陆就可以访问的路由)
if (whiteList.indexOf(to.path) !== -1) {
next()
} else {
// 其他的一律给我跳到login页面 老老实实的进行登陆
next(`/login?redirect=${to.path}`)
NProgress.done()
}
}
})
利用beforeEach进行访问拦截,如果没登录,跳转到登录页面进行登录。
user/getInfo:src/store/modules/user.js
image.png
getInfo用于获取用户信息并保存到vuex中,后面是一堆的数据校验。
permission/generateRoutes:src/store/modules/permission.js
// 判断是否有权限
function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
// roles.some => Array.some 相当于是只要有一个满足就为true
// 判断用户的权限于当前路由访问所需要的权限是否有一个满足
// 比如说用户权限为 ['one','two'] 当前路由访问所需要权限为 ['two','three'] 那么就说明当前用户可以访问这个路由
return roles.some(role => route.meta.roles.includes(role))
} else {
// 默认是可访问的
return true
}
}
// 生成可访问路由
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {
const tmp = { ...route }
// 判断当前路由是否可以访问
if (hasPermission(roles, tmp)) {
// 如果当前路由还有子路由
if (tmp.children) {
// 进行递归处理
tmp.children = filterAsyncRoutes(tmp.children, roles)
}
// 将可访问路由放入数组中
res.push(tmp)
}
})
// 返回
return res
}
// 为什么要写这里呢,因为后面的Sidebar组件与这个环环相扣
const mutations = {
SET_ROUTES: (state, routes) => {
// 添加的路由
state.addRoutes = routes
// 将vuex中的路由进行更新
state.routes = constantRoutes.concat(routes)
}
}
const actions = {
generateRoutes({ commit }, roles) {
return new Promise(resolve => {
let accessedRoutes
// 如果roles包含 'admin' 直接可以全部访问
if (roles.includes('admin')) {
accessedRoutes = asyncRoutes || []
} else {
// 利用 filterAsyncRoutes 过滤出可访问的路由
accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
}
// 保存可访问的路由到store中
commit('SET_ROUTES', accessedRoutes)
// 将可访问路由返回
resolve(accessedRoutes)
})
}
}
generateRoutes就是根据得到的用户权限生成可以访问的路由。
动态路由实现
router.addRoutes(accessRoutes)
动态路由的核心代码就这一句话,短小精悍,其他的都是为了完成动态路由做的一些 “准备工作” ,user/getInfo获取用户信息得到用户的roles权限数组,返回user/generateRoutes生成可访问路由,就是这么神奇的一步,实现了动态路由。
三、总结
1 Login/index.vue点击登陆 提交user/login的actions。
2 user/login进行登陆验证,登陆成功之后保存token到vuex和cookie中。
3 回到Login/index.vue跳转路由,这时就到了permission.js
4 permission.js进行判断是否登陆,是否有用户信息?没有用户信息就获取用户信息,并且保存到vuex,然后根据用户信息中的roles生成可访问路由,并通过addRoutes进行添加。
登录流程图
image.png
网友评论