«

记前端的权限控制方案

codeez 发布于 阅读:2175 笔记


登录鉴权、访问权限、页面权限、按钮权限
在开发后端管理系统时,权限管理是十分常见的,大致可分为接口权限、路由权限、菜单权限、按钮权限四类。
【菜单权限和路由权限基本就是一起绑定的,有的是通过后端返回路由配置,有的是前端把路由全部写死,看项目大小,一般小项目前端可以写死】

一、接口权限

通常使用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>

前端

请先 登录 再评论