面试官:vue权限管理该怎么做?控制到按钮级别的权限呢?

文摘   2024-11-08 10:51   陕西  

今天聊聊一个经典的面试题:Vue项目中,怎么做权限管理?尤其是如果要控制到按钮级别的权限,又该怎么做呢?看起来像个基本操作,但细细一想,真是个“内有乾坤”的技术活儿。今天,我就用程序员的思维带大家拆解一下 Vue 权限管理的“全家桶”。

咱们先从权限这个概念聊起吧。权限管理嘛,本质就是控制用户能访问什么、不能触碰什么,确保大家各守其位。对于前端来说,权限控制可以简单分为页面级按钮级,即用户能不能打开某个页面,能不能点击某个按钮。说白了,前端权限管理就是控制请求的发起权,毕竟绝大多数权限请求都来源于页面加载和用户点击。

掌控页面访问:路由权限控制

要控制页面访问,咱们得先盯住路由。实现路由级别的权限控制,可以分成两种方式:

1. 全局加载+路由守卫控制

这一方式算是“满汉全席”了,即把所有路由全挂上,然后在全局路由守卫里“严防死守”。比如,可以给每个路由添加meta字段来标识访问权限。每次跳转前,我们根据用户的角色与路由的meta.roles进行比对,看是否符合权限要求:

const routerMap = [
  {
    path'/admin',
    component: AdminPage,
    meta: { roles: ['admin'] }
  },
  {
    path'/user',
    component: UserPage,
    meta: { roles: ['user''admin'] }
  }
];

router.beforeEach((to, from, next) => {
  const userRoles = store.getters.roles;
  if (hasPermission(userRoles, to.meta.roles)) {
    next();
  } else {
    next('/403');
  }
});

function hasPermission(userRoles, routeRoles{
  return routeRoles ? routeRoles.some(role => userRoles.includes(role)) : true;
}

上面的代码就很直观了:用户角色只要匹配到路由的roles字段,就能访问。要是“无权越级”,系统就把他丢到403提示页去。这个方法看似简单,但因为每次跳转都要检查权限,路由挂载多了会有点卡。

2. 按需加载+动态挂载

想让系统更灵活些?那就试试按需加载吧!用户登录后,咱们可以通过接口获取他的角色信息,再根据角色筛选有权访问的路由,动态挂载需要的部分。用 Vue Router 的addRoutes来动态挂载路由:

import router from './router';
import store from './store';

router.beforeEach((to, from, next) => {
  if (!store.getters.isLoggedIn) {
    next('/login');
  } else {
    if (store.getters.userRoles.length === 0) {
      store.dispatch('GetUserRoles').then(() => {
        router.addRoutes(store.getters.filteredRoutes);
        next({ ...to, replacetrue });
      });
    } else {
      next();
    }
  }
});

这种按需挂载的方式大大减少了初始加载时的路由数量,而且新路由只在用户有权限时才添加,既减小了页面开销,还确保了用户看不到自己无权访问的页面。

想按需显示按钮?没问题!

按钮权限控制其实就是更细化的权限管理。最简单粗暴的方法是在按钮组件上直接用v-if进行判断,不过呢,页面一多就要手动写很多v-if。所以更聪明的做法是自定义一个指令,这样我们只需给按钮加个指令,权限鉴定就能由它自动处理。

自定义指令方式:

下面是个简单的自定义指令代码,通过检测用户权限控制按钮显示与否:

import Vue from 'vue';

Vue.directive('has', {
  inserted(el, binding) => {
    const userPermissions = store.getters.permissions;
    if (!userPermissions.includes(binding.value)) {
      el.parentNode && el.parentNode.removeChild(el);
    }
  }
});

// 使用指令
<el-button @click="edit" v-has="'editPermission'">编辑</el-button>

在这个指令中,我们用binding.value传入按钮权限,比如“编辑”的权限可能是editPermission。指令会检查用户的权限数组里是否包含这个权限,没有的话直接移除按钮。

菜单权限:前后端合作一波

权限管理里另一个常见问题是菜单显示。大家可以选择让菜单和路由一起在前端定义,也可以让后端把菜单数据传过来。这里就介绍下常用的前后端分离菜单权限方案。

菜单-路由分离方案

菜单和路由分离后,菜单数据由后端返回。前端只需根据后端传回来的菜单项和权限信息动态生成可见菜单,并挂载相应路由。

// 前端仅保留路由组件,不定义权限
const routeComponents = {
  Home() => import('@/pages/Home.vue'),
  Admin() => import('@/pages/Admin.vue'),
};

axios.get('/api/menu').then(response => {
  const menuRoutes = response.data.menu.map(menuItem => ({
    path: menuItem.path,
    component: routeComponents[menuItem.component],
    name: menuItem.name,
  }));
  router.addRoutes(menuRoutes);
});

前端拿到菜单数据后,直接根据字段生成动态路由表,然后添加到Vue Router实例。这样不仅简化了前端代码,还避免了前端每次改动菜单都要重编译发布。

接口权限:终极兜底

虽然有了路由和按钮的权限限制,但万一漏网之鱼偷偷发起一个越权请求怎么办?所以最后一道防线——接口权限拦截,少不了。在前端可以通过axios请求拦截器来加个“检查岗”,凡是没有权限的请求一律打回重做。

axios.interceptors.request.use(config => {
  config.headers['Authorization'] = 'Bearer ' + store.getters.token;
  return config;
});

axios.interceptors.response.use(
  response => response,
  error => {
    if (error.response.status === 401) {
      router.push('/login');
    }
    return Promise.reject(error);
  }
);

上面代码做了两件事:一是给每个请求加上Authorization头部,二是遇到401状态直接踢回登录页,确保越权请求无所遁形。

面试官问 Vue 的权限管理怎么做,咱们直接从路由权限、按钮权限、菜单权限和接口权限四个方面来分层次解释:

  1. 路由权限控制:通过在路由里配置角色信息,然后用beforeEach路由守卫在跳转前进行权限验证,确保用户只能访问自己有权访问的页面。

  2. 按钮权限控制:建议用自定义指令来实现按钮级别的权限判断,更灵活方便。指令会自动控制按钮显示,有效减少繁琐的v-if判断。

  3. 菜单权限:前端通过与后端合作,动态生成用户权限范围内的菜单,实现了菜单与路由的解耦。

  4. 接口权限:最后用axios的请求拦截器设置权限控制,确保无论是页面加载还是按钮点击,所有请求都受控。

目前,对编程、职场感兴趣的同学,大家可以联系我微信:golang404,拉你进入“程序员交流群”。

虎哥私藏精品 热门推荐

虎哥作为一名老码农,整理了全网最前端资料合集

资料包含了《前端面试题PDF合集》、《前端学习视频》、《前端项目及源码》,总量高达108GB。

全部免费领取全面满足各个阶段程序员的学习需求!

web前端专栏
回复 javascript,获取前端面试题。分享前端教程,AI编程,AI工具,Tailwind CSS,Tailwind组件,javascript教程,webstorm教程,html教程,css教程,nodejs教程,vue教程。
 最新文章