记前端的权限控制方案
登录鉴权、访问权限、页面权限、按钮权限
在开发后端管理系统时,权限管理是十分常见的,大致可分为接口权限、路由权限、菜单权限、按钮权限四类。
【菜单权限和路由权限基本就是一起绑定的,有的是通过后端返回路由配置,有的是前端把路由全部写死,看项目大小,一般小项目前端可以写死】
一、接口权限
通常使用JWT实现。
认证机制:用户登陆成功后,服务端生成token交给前端,前端每次请求接口时带上token。一般在封装axios的拦截器中带上token,在响应拦截中处理token无效、过期等问题。
axios.interceptors.request.use(config => {
config.headers['Authorization'] = getToken(); // 请求拦截带上token
return config;
});
axios.interceptors.response.use(response => {
if (response.data.code === 401) {
// token过期处理逻辑,跳转登录页
}
return response;
}, error => {
if (error.response.status === 401) {
// token无效等
}
return Promise.reject(error);
});
二、路由权限控制
通常使用路由守和与动态路由实现,还可以在路由配置中增加元信息字段,如roles,用来标识该路由需要的权限【这种是比较简单的处理方案】
【中大型项目一般是后端返回路由,前端动态添加路由】
// 为单个路由增加路由守卫
const router = new VueRouter({
routes: [
{
path: '/admin',
component: Admin,
beforeEnter: (to, from, next) => {
if (hasRole('admin')) { // 判断用户是否有admin权限
next();
} else {
next({ path: '/403' });
}
}
},
{
path: '/403',
component: () => import('@/views/403.vue')
}
]
});
// 全局路由守卫
// 定义路由
const routes = [
{
path: '/admin',
name: 'AdminDashboard',
component: AdminDashboard,
meta: { requiresAuth: true, role: 'admin' }
},
{
path: '/public',
name: 'PublicPage',
component: PublicPage,
meta: { requiresAuth: false }
}
];
// 创建路由实例
const router = new Router({
mode: 'history',
routes
});
// 全局前置守卫用来根据角色生成路由
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// 这里假设从store中获取用户角色
const userRole = store.state.user.role;
if (to.meta.role && to.meta.role !== userRole) {
// 如果用户角色不匹配,重定向到对应的页面
next({ path: '/login' }); // 假设有一个登录页面
} else {
next(); // 允许访问
}
} else {
next(); // 不需要认证的路由,直接访问
}
});
三、菜单权限控制
通常与路由权限控制搭配使用。服务端返回菜单列表,前端渲染。
const menuData = await getUserInfo();
const accessMenu = menuData.accessMenu; // 返回菜单列表,将列表转换为路由表
const routerMap = accessMenu.map(item => ({
path: item.path,
component: () => import(`@/views/${item.component}.vue`),
name: item.name,
meta: { roles: item.roles }
}));
router.addRoutes(routerMap); // 动态路由刷新后清空,所以可在vuex中配合localStorage进行持久化存储
四、按钮权限控制
较简单的场景下可直接使用v-if,但通常还是会使用自定义指令,比如创建一个名为v-has的自定义指令来封装按钮权限的处理逻辑。
Vue.directive('has', {
bind: function (el, binding, vnode) {
const { value } = binding;
const permissions = 从pinia中取出后端返回的按钮权限集合; // 判断集合内有无权限
if (!value || !permissions.includes(value)) {
el.parentNode.removeChild(el);
}
}
});
// 使用
<el-button @click="editClick" type="primary" v-has="'system:home:edit'">编辑</el-button>