前端的权限校验可以分为菜单资源校验、接口资源和按钮资源校验。
菜单权限
把接口权限和按钮权限统称为资源权限,在数据库中以字段区分,然后把菜单权限也存入数据库,把登录和获取资源权限等接口加入白名单,当登录成功后,在路由切换中调用远程路由,即菜单权限和资源权限,菜单权限数据转换格式存入 store 然后再生成菜单。
这样本地就无需再声明一堆路由,直接从数据库读取。更加方便快捷。
接口权限校验
登录后去调取用户的菜单权限和资源权限,然后把菜单权限动态添加为路由,资源权限转换格式存入 store,然后把登录和调用用户权限的接口加入白名单,再在 axios 的拦截器中添加拦截校验,从 store 中取出存储好的资源权限,与当前访问的接口地址进行对比,如对比结果为 true 则表示可以访问,否则提示没有权限跳转到 401 页面。
按钮权限校验
网上大部分教程都是通过挂在一个检测函数,然后在按钮上通过这个函数来处理按钮的显示与隐藏。
定义好对比函数后,在按钮上调用函数,传入资源权限的 title 来与本地存储的权限列表进行对比,有则显示,无则隐藏。
具体实现
路由切换和检测,异步获取路由拼装右侧菜单和资源权限。
const _import = require('@/router/_import')
let routers
router.beforeEach((to, form, next) => {
Nprogress.start()
if (get('token')) {
if (setter.whiteList.indexOf(to.path) !== -1) {
to.path === '/login' ? next({ path: '/' }) : next()
NProgress.done()
} else {
!routers
? !store.getters.addRouters.length || store.getters.addRouters.length === 0
? getAsyncRouter(to, next)
: routerGo(to, next, store.getters.permission_routers)
: (next(), Nprogress.done())
}
} else {
/* 白名单校验 */
setter.whiteList.indexOf(to.path) !== -1 ? next() : next(`/login?redirect=${to.path}`)
Nprogress.done()
}
})
function routerGo(to, next, data) {
routers = filterAsyncRouter(data)
store.dispatch('GenerateRoutes', { routers }).then(() => {
// 404只能放在异步路由,否则造成刷新404
routers.push({ path: '*', redirect: '/404', hidden: true })
router.addRoutes(routers)
next({ ...to, replace: true })
Nprogress.done()
})
}
// 遍历后台传来的路由字符串,转换为组件对象
function filterAsyncRouter(asyncRouterMap) {
const accessedRouters = asyncRouterMap.filter(route => {
if (route.component) route.component = route.component === 'Layout' ? Layout : _import(route.component)
if (route.children && route.children.length) route.children = filterAsyncRouter(route.children)
return true
})
return accessedRouters
}
function getAsyncRouter(to, next) {
getPermissions().then(response => {
routerGo(to, next, response.data.menus)
saveElement(response.data.elements)
}).catch((error) => {
console.log(error)
})
}
/**
* 转换资源格式并存储
* @param elements
*/
// todo 优化
function saveElement(elements) {
const requests = {}; const buttons = {}; const localHash = { requests, buttons }
elements.forEach(item => {
requests[item.method.toLowerCase() + ',' + item.path] = true
buttons[item.title] = true
})
store.dispatch('GenerateElementss', { localHash })
}
import
module.exports = file => require('@/views/' + file + '.vue').default
接口校验
service.interceptors.request.use(
config => {
const localElement = store.getters.elements
const url = config.url.replace(/[/]+[0-9]+[/]/g, '/*/')
if ((!localElement.requests && setter.httpWhiteList.indexOf(url) === -1) ||
(localElement.requests && !localElement.requests[config.method + ',' + url])) {
router.push({ name: '401' })
return Promise.reject('没有访问权限')
} else {
store.getters.token && (config.headers[setter.tokenName] = get('token'))
const data = {
...(config.method === 'get' || config.method === 'GET' ? (config.params || {}) : (config.data || {})),
sequenceId: uuid().replace(/-/g, ''),
timestamp: new Date().getTime()
}
const sign = common.getSign(setter.apiKey, data)
config.method === 'get' || config.method === 'GET' ? (config.params = {
...(data || {}),
sign: md5(sign)
}) : (config.data = {
...(data || {}),
sign: md5(sign)
})
return config
}
},
error => {
if (setter.debug) {
console.log(error)
}
Promise.reject(error.message)
}
)
资源校验
在需要校验权限的按钮上添加 v-has="'posts_add'"
即可
Vue.directive('has', {
bind: function(el, binding, vnode) {
(!Vue.prototype.$_has(binding.value)) && el.parentNode.removeChild(el)
}
})
Vue.prototype.$_has = function(value) {
const elements = store.getters.elements
if (!elements || !elements.buttons) return false
if (elements.buttons[value]) return true
return false
}
具体的实现大概就是如此了,菜单和资源的格式以及获取后在本地的存储都可以根据自己实际情况来处理,没必要照搬。
网友评论