一个管理项目一般需要将:1.登录功能 2.权限验证功能 分离
登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token
,拿到token
之后(我会将这个token
存到cookie
中,保证刷新页面后能记住用户登录状态),前端会根据token
再去拉取一个 user_info
的接口来获取用户的详细信息(如用户权限,用户名等等信息)
权限验证:通过token
获取用户对应的 role
,动态根据用户的 role
算出其对应有权限的路由,通过 router.addRoutes
动态挂载这些路由
上述所有的数据和操作都是通过vuex
全局管理控制的。(补充说明:刷新页面后 vuex
的内容也会丢失,所以需要重复上述的那些操作)接下来。
登录 + 获取用户信息
权限验证 权限控制的整体思路:
前端有一张路由表,表示每一个路由可访问的权限。
用户登录之后,通过token
获取用户的role
,动态根据用户的role
算出其对应的权限路由,再通过router.addRoutes
动态挂载路由
但这些控制都只是页面级的,说白了前端再怎么做权限控制都不是绝对安全的,后端的权限验证是逃不掉的。
主流方式:
前端控制页面级权限(不同用户显示不同的侧边栏、限制其能进入的页面);
后端则会验证每一个涉及请求的操作,验证其是否有该操作的权限,每一个后台的请求不管是 get
还是 post
都会让前端在请求 header
里面携带用户的 token
【request.js
文件中请求拦截器中添加】 ,后端会根据该 token
来验证用户是否有权限执行该操作。(若没有权限则抛出一个对应的状态码,前端检测到该状态码,做出相对应的操作)
具体实现
创建vue
实例的时候将vue-router
挂载,但这个时候vue-router
挂载一些登录或者不用权限的公用的页面。
当用户登录后,获取用role
,将role
和路由表每个页面的需要的权限作比较,生成最终用户可访问的路由表。
调用router.addRoutes(store.getters.addRouters)
添加用户可访问的路由。
使用vuex
管理路由表,根据vuex
中可访问的路由渲染侧边栏组件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import Vue from 'vue' ;import Router from 'vue-router' ;import Login from '../views/login/' ;const dashboard = resolve => require (['../views/dashboard/index' ], resolve);](https : export const constantRouterMap = [ { path : '/login' , component : Login }, { path : '/' , component : Layout , redirect : '/dashboard' , name : '首页' , children : [{ path : 'dashboard' , component : dashboard }] }, ] export default new Router ({ routes : constantRouterMap }); export const asyncRouterMap = [ { path : '/permission' , component : Layout , name : '权限测试' , meta : { role : ['admin' ,'super_editor' ] }, children : [ { path : 'index' , component : Permission , name : '权限测试页' , meta : { role : ['admin' ,'super_editor' ] } }] }, { path : '*' , redirect : '/404' , hidden : true } ];
这里我们根据 vue-router官方推荐
的方法通过meta
标签来标示改页面能访问的权限有哪些。如meta: { role: ['admin','super_editor'] }
表示该页面只有 admin 和 超级编辑 才能有资格进入。
注意事项:这里有一个需要非常注意的地方就是 404 页面 一定要最后加载 ,如果放在constantRouterMap一同声明了404,后面的所以页面都会被拦截到404
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 import router from './router' import store from './store' import { Message } from 'element-ui' import NProgress from 'nprogress' import 'nprogress/nprogress.css' import { getToken } from '@/utils/auth' import { isRelogin } from '@/utils/request' NProgress .configure ({ showSpinner : false })const whiteList = ['/login' , '/auth-redirect' , '/bind' , '/register' ]router.beforeEach ((to, from , next ) => { NProgress .start () if (getToken ()) { to.meta .title && store.dispatch ('settings/setTitle' , to.meta .title ) if (to.path === '/login' ) { next ({ path : '/' }) NProgress .done () } else { if (store.getters .roles .length === 0 ) { isRelogin.show = true store.dispatch ('GetInfo' ).then (() => { isRelogin.show = false store.dispatch ('GenerateRoutes' ).then (accessRoutes => { router.addRoutes (accessRoutes) next ({ ...to, replace : true }) }) }).catch (err => { store.dispatch ('LogOut' ).then (() => { Message .error (err) next ({ path : '/' }) }) }) } else { next () } } } else { if (whiteList.indexOf (to.path ) !== -1 ) { next () } else { next (`/login?redirect=${to.fullPath} ` ) NProgress .done () } } }) router.afterEach (() => { NProgress .done () })
小提一下:按钮权限的控制 首先按钮的权限可以使用v-if
进行判断,但是如果页面过多,每个页面都需要获取用户权限role
和路由表里的meta.btnPermissions
,然后再做判断。
①配置路由表中的btnPermissions
(设置元信息) ②自定义权限指令 ③在标签中使用v-has