前端 角色的权限控制(RBAC)

119 阅读3分钟

我们将分几个步骤来实现这个功能:

  1. 定义角色和权限模型
  2. 设置后端接口以获取用户信息
  3. 使用 Vuex 管理用户状态
  4. 配置 Vue Router 以实现动态路由加载
  5. 创建动态菜单
  6. 实现按钮级别的权限控制

1. 角色和权限模型定义

首先,需要定义一个清晰的角色和权限模型。这通常涉及到以下几个概念:

  • 角色 (Role)admineditorviewer等。
  • 权限 (Permission) :每个角色可能拥有的具体权限,比如viewDashboardeditArticledeleteArticleviewUsersmanageRoles等。
  • 资源 (Resource) :可以是页面、API 或者按钮等。

2. 用户认证和授权

在实际应用中,通常需要有一个后端 API 来处理用户的登录认证和获取用户角色。一旦用户登录成功,他们的角色信息应该存储在本地(例如使用 Vuex 或 localStorage)。

示例:获取用户角色


// 假设我们有一个 API 接口来获取用户信息
    async function fetchUserInfo() {
        const response = await axios.get('/api/user/info');
        return response.data;
    }
    
# 或者

// mock.js
    
 export async function fetchUserInfo() {
    // 模拟从服务器获取用户信息
    return {
        id: 1,
        username: 'admin',
        roles: ['admin'],
        permissions: ['viewDashboard', 'editArticle', 'deleteArticle', 'viewUsers', 'manageRoles'],
        };
    
 }


3. 使用 Vuex 管理用户状态

我们需要使用Vuex来存储和管理用户的信息和权限。(如登录状态、角色等)是一种很好的做法,这样可以方便地在整个应用中共享这些数据。


// store/index.js

import Vue from 'vue';
import Vuex from 'vuex';
import { fetchUserInfo } from './mock';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    user: {},
    permissions: [],
  },
  
  getters: {
    isLoggedIn: state => !!state.user.id,
    hasRole: state => role => state.user.roles.includes(role),
    hasPermission: state => permission => state.permissions.includes(permission),
  },
  
  mutations: {
    setUser(state, user) {
      state.user = user;
    },
    
    setPermissions(state, permissions) {
      state.permissions = permissions;
    },
  },
  
  actions: {
    async fetchUser({ commit }) {
      const user = await fetchUserInfo();
      commit('setUser', user);
      commit('setPermissions', user.permissions);
    },
  },
});

 4. 配置 Vue Router 以实现动态路由加载

Vue Router配置中,为每个路由添加一个元信息(meta)字段 来指定访问该路由所需的权限。同时使用Vue Router的全局前置守卫(beforeEach)来检查用户是否有访问当前路由所需的权限。



// router/index.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import store from '../store';

6Vue.use(VueRouter);

const routes = [
  {
    path: '/',
    component: () => import('../layout/MainLayout.vue'),
    children: [
      // 这里只定义了路由的基础结构,具体的权限会在下面进行动态添加
      {
        path: 'dashboard',
        name: 'Dashboard',
        component: () => import('../views/Dashboard.vue'),
        meta: { requiresAuth: ['viewDashboard'] },
      },
      // 其他路由...
    ],
  },
  
  // 未授权路由
  {
    path: '/unauthorized',
    name: 'Unauthorized',
    component: () => import('../views/Unauthorized.vue'),
  },
  
  // 404 路由
  {
    path: '*',
    redirect: { name: 'Unauthorized' },
  },
];

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
});

router.beforeEach(async (to, from, next) => {
 
 // 如果已经登录,则直接进入
  if (store.getters.isLoggedIn) {
    next();
  } else {
    // 尝试登录
    await store.dispatch('fetchUser');

    // 检查是否有权限访问目标路由
    if (to.meta.requiresAuth) {
      const hasPermission = to.meta.requiresAuth.some(permission => store.getters.hasPermission(permission));
      if (hasPermission) {
        next();
      } else {
        next({ name: 'Unauthorized' });
      }
    } else {
      next();
    }
  }
});

export default router;

5. 创建动态菜单

在应用的主布局中添加动态生成的菜单项。

<!-- layout/MainLayout.vue -->

<template>
  <div class="main-layout">
    <nav>
      <ul>
        <li v-for="(item, index) in menuItems" :key="index" :class="{ active: $route.name === item.name }">
          <router-link :to="item.path">{{ item.title }}</router-link>
        </li>
      </ul>
    </nav>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  data() {
    return {
      menuItems: [
        { title: '仪表盘', path: '/dashboard', name: 'Dashboard', requiresAuth: ['viewDashboard'] },
        // 其他菜单项...
      ],
    };
  },
  computed: {
    filteredMenuItems() {
      return this.menuItems.filter(item => this.hasPermission(item.requiresAuth));
    },
  },
  methods: {
    hasPermission(permissions) {
      return permissions.every(permission => this.$store.getters.hasPermission(permission));
    },
  },
};
</script>

6. 动态显示按钮和组件

为了动态地显示或隐藏某些按钮或组件,可以使用 Vue 的 v-ifv-show 指令来控制它们的可见性。

示例:动态显示按钮

<template>
    <div> 
        <h1>仪表盘</h1>

        <!-- 只有具备 'write' 权限的用户才能看到此按钮 --> 
        <button v-if="user.permissions.includes('write')">编辑</button> 
    
        <!-- 只有具备 'delete' 权限的用户才能看到此按钮 --> 
        <button v-if="user.permissions.includes('delete')">删除</button> 
    
    </div> 
</template> 
<script> 
export default { 
methods: {
    hasPermission(permission) {
      return this.$store.getters.hasPermission(permission);
    },
  },