vue2/vue3前端创建脚手架并引入RBAC权限模型

创建Vue脚手架项目

使用Vue CLI创建新项目(Vue2或Vue3根据需求选择):

bash 复制代码
# Vue3
vue create vue3-rbac-demo
# 或Vue2
vue create vue2-rbac-demo

安装必要依赖:

bash 复制代码
npm install axios vue-router vuex element-ui # Vue2
# 或
npm install axios vue-router@4 vuex@4 element-plus # Vue3

目录结构设计

复制代码
src/
├── api/                # 接口封装
│   ├── auth.js         # 认证相关接口
│   ├── user.js         # 用户相关接口
│   └── index.js        # 接口统一导出
├── router/             # 路由配置
│   ├── index.js        # 路由入口
│   └── permission.js   # 路由权限控制
├── store/              # Vuex状态管理
│   ├── modules/        # 模块化store
│   │   ├── auth.js     # 认证模块
│   │   └── user.js     # 用户模块
│   └── index.js        # store入口
├── utils/              # 工具函数
│   ├── auth.js         # 权限相关工具
│   └── request.js      # axios封装
├── views/              # 页面组件
│   ├── layout/         # 布局组件
│   ├── login/          # 登录页
│   └── ...             # 其他业务页面
└── main.js             # 应用入口

封装axios请求

utils/request.js 基础配置:

javascript 复制代码
import axios from 'axios'
import { Message } from 'element-ui' // Vue2
// import { ElMessage } from 'element-plus' // Vue3

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 5000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    if (res.code !== 200) {
      Message.error(res.message || 'Error')
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    Message.error(error.message || 'Error')
    return Promise.reject(error)
  }
)

export default service

RBAC权限模型实现

store/modules/auth.js Vuex模块:

javascript 复制代码
const state = {
  roles: [],
  permissions: []
}

const mutations = {
  SET_ROLES: (state, roles) => {
    state.roles = roles
  },
  SET_PERMISSIONS: (state, permissions) => {
    state.permissions = permissions
  }
}

const actions = {
  // 获取用户权限信息
  getInfo({ commit }) {
    return new Promise((resolve, reject) => {
      getUserInfo().then(response => {
        const { roles, permissions } = response.data
        commit('SET_ROLES', roles)
        commit('SET_PERMISSIONS', permissions)
        resolve({ roles, permissions })
      }).catch(error => {
        reject(error)
      })
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

路由权限控制

router/permission.js 路由守卫:

javascript 复制代码
import router from './index'
import store from '../store'
import { getToken } from '../utils/auth'

const whiteList = ['/login'] // 免登录白名单

router.beforeEach(async (to, from, next) => {
  const hasToken = getToken()
  
  if (hasToken) {
    if (to.path === '/login') {
      next({ path: '/' })
    } else {
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
      if (hasRoles) {
        next()
      } else {
        try {
          const { roles } = await store.dispatch('auth/getInfo')
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          router.addRoutes(accessRoutes)
          next({ ...to, replace: true })
        } catch (error) {
          await store.dispatch('auth/resetToken')
          next(`/login?redirect=${to.path}`)
        }
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      next()
    } else {
      next(`/login?redirect=${to.path}`)
    }
  }
})

动态路由生成

store/modules/permission.js

javascript 复制代码
import { asyncRoutes, constantRoutes } from '@/router'

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

// 根据角色过滤路由
function filterAsyncRoutes(routes, roles) {
  const res = []
  routes.forEach(route => {
    const tmp = { ...route }
    if (hasPermission(roles, tmp)) {
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}

function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

权限指令实现

utils/permission.js 自定义指令:

javascript 复制代码
import store from '@/store'

function checkPermission(el, binding) {
  const { value } = binding
  const roles = store.getters && store.getters.roles

  if (value && value instanceof Array) {
    if (value.length > 0) {
      const hasPermission = roles.some(role => {
        return value.includes(role)
      })

      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el)
      }
    }
  } else {
    throw new Error(`需要指定权限数组,如v-permission="['admin']"`)
  }
}

export default {
  inserted(el, binding) {
    checkPermission(el, binding)
  },
  update(el, binding) {
    checkPermission(el, binding)
  }
}

在main.js中注册指令:

javascript 复制代码
import permission from './utils/permission'

// Vue2
Vue.directive('permission', permission)

// Vue3
app.directive('permission', permission)

使用示例:

html 复制代码
<button v-permission="['admin']">管理员按钮</button>

接口封装示例

api/user.js

javascript 复制代码
import request from '@/utils/request'

export function login(data) {
  return request({
    url: '/auth/login',
    method: 'post',
    data
  })
}

export function getInfo() {
  return request({
    url: '/user/info',
    method: 'get'
  })
}

export function logout() {
  return request({
    url: '/auth/logout',
    method: 'post'
  })
}

登录页面实现

views/login/index.vue

javascript 复制代码
<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules">
      <el-form-item prop="username">
        <el-input v-model="loginForm.username" placeholder="用户名" />
      </el-form-item>
      <el-form-item prop="password">
        <el-input v-model="loginForm.password" type="password" placeholder="密码" />
      </el-form-item>
      <el-button type="primary" @click="handleLogin">登录</el-button>
    </el-form>
  </div>
</template>

<script>
import { login } from '@/api/auth'

export default {
  name: 'Login',
  data() {
    return {
      loginForm: {
        username: '',
        password: ''
      },
      loginRules: {
        username: [{ required: true, trigger: 'blur', message: '请输入用户名' }],
        password: [{ required: true, trigger: 'blur', message: '请输入密码' }]
      }
    }
  },
  methods: {
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          login(this.loginForm).then(response => {
            this.$store.commit('auth/SET_TOKEN', response.token)
            localStorage.setItem('token', response.token)
            this.$router.push({ path: '/' })
          }).finally(() => {
            this.loading = false
          })
        }
      })
    }
  }
}
</script>

主布局实现

views/layout/index.vue

javascript 复制代码
<template>
  <div class="app-wrapper">
    <sidebar class="sidebar-container" />
    <div class="main-container">
      <navbar />
      <app-main />
    </div>
  </div>
</template>

<script>
import { Navbar, Sidebar, AppMain } from './components'

export default {
  name: 'Layout',
  components: {
    Navbar,
    Sidebar,
    AppMain
  },
  computed: {
    sidebar() {
      return this.$store.state.app.sidebar
    }
  }
}
</script>

权限按钮组件

components/PermissionButton.vue

javascript 复制代码
<template>
  <div v-if="hasPermission">
    <slot />
  </div>
</template>

<script>
export default {
  name: 'PermissionButton',
  props: {
    permission: {
      type: Array,
      required: true
    }
  },
  computed: {
    hasPermission() {
      return this.permission.some(role => {
        return this.$store.getters.roles.includes(role)
      })
    }
  }
}
</script>

使用示例:

javascript 复制代码
<permission-button :permission="['admin']">
  <button>管理员操作</button>
</permission-button>

环境变量配置

.env.development

bash 复制代码
VUE_APP_BASE_API = '/api'

.env.production

bash 复制代码
VUE_APP_BASE_API = 'https://api.example.com'

完整RBAC流程

  1. 用户登录获取token并存储
  2. 获取用户角色和权限信息存入Vuex
  3. 根据角色生成可访问的路由表
  4. 路由守卫控制页面访问权限
  5. 指令/组件控制按钮级权限
  6. 接口请求携带token进行验证
  7. token过期或权限变更时重置状态

这个实现方案完整覆盖了RBAC模型在前端应用中的核心功能点,包括路由级权限控制和按钮级权限控制,可以根据实际项目需求进行调整和扩展。

相关推荐
计算机学姐2 小时前
基于php的摄影网站系统
开发语言·vue.js·后端·mysql·php·phpstorm
OpenTiny社区2 小时前
🎉 TinySearchBox 重磅更新:支持 Vue2,一次满足我的所有需求!
前端·javascript·vue.js
JIngJaneIL2 小时前
基于java+ vue学生选课系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
小周同学:2 小时前
vue2 +driver 实现页面导航提示引导
vue.js
Nick_zcy2 小时前
基于Vue和Python的羽毛球拍智能推荐系统, 从“不会选羽毛球拍”到“选对拍”的一站式小工具
前端·vue.js·python·算法·推荐算法
菠菜盼娣3 小时前
vue3知识点
前端·vue.js
JIngJaneIL3 小时前
基于java+ vue建筑材料管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot
一 乐3 小时前
办公系统|基于springboot + vueOA办公管理系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·spring
Gazer_S3 小时前
【Vue Router 路由守卫(Navigation Guards)指南:概念、执行顺序、beforeResolve、异步路由组件】
前端·javascript·vue.js