Vue3权限控制全攻略:路由与组件层面的用户角色与权限管理方法深度解析

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: Vue3权限控制全攻略:路由与组件层面的用户角色与权限管理方法深度解析

一、权限控制

随着前端技术的不断发展,越来越多的前端框架被使用在 Web 应用程序中,其中尤为出色的一个就是 Vue。Vue 是一个易于理解并且使用方便的框架,它被广泛地应用于 Web 应用程序的开发中。在大多数 Web 应用程序中,权限控制是至关重要的一部分,如何在 Vue 中进行权限控制就成为了一个十分关键的问题。


权限控制是一个很重要的概念,在 Web 应用程序中尤其重要。简单地说,权限控制就是将用户分为不同的分类,为每个分类分配相应的用户权限。这样,用户就只能访问他们所允许的内容了。权限控制可以提高应用程序的安全性和稳定性,使数据更加安全可靠。


在 Vue 中进行权限控制,通常有两种方式:第一种是在路由层面进行控制,第二种是在组件层面进行控制。


用户登录后该用户的角色与权限信息会一同返回给前端,前端将这些信息存储到状态管理里备用即可。

  • 这里使用pinia存储当前用户的角色及权限。你可以根据实际情况进行相应的调整。
  • store/index.js
import { defineStore } from 'pinia'
import { ref } from 'vue';

export const userPermissionsStore = defineStore('userPermissions', () => {
  // 角色
  const roles = ref('admin')

  // 权限
  const userPermissions = ref([])

  //是否登录
  const isLogin = ref(false)

  // 设置状态(传入的权限信息赋值给该状态)
  const setUserPermissions = (params) => {
    userPermissions.value = params
  }

  return {
    isLogin,
    userPermissions,
    roles,
    setUserPermissions
  }
})


二、路由层面控制

在路由层面进行控制,可以在路由的元数据 meta 中设置用户权限,然后可以在路由守卫函数中进行校验。如果当前用户的权限符合该路由的要求,则策略继续进行,否则将导航到其他页面。

路由的元数据 meta 中设置了 requireAuth 和 roles 两个属性,requireAuth 表示该路由需要用户登录才能访问,roles 表示受访问限制的角色。可以在 beforeEach 路由守卫函数中校验用户权限,如果用户有访问该路由的权限,则进入页面,否则跳转到其他页面。这样,就可以在路由层面进行权限控制了。

import { createRouter, createWebHashHistory } from 'vue-router'
import { userPermissionsStore } from '@/store/index'
import { storeToRefs } from 'pinia'

let routes = [{
  path: '/home', // 路径
  name: 'home', // 路由名称
  component: () => import('../views/home.vue'),
  meta: {
    requireAuth: true, // 需要用户权限
    roles: ['admin', 'guest'] // 受访问限制的角色
  }
}, {
  path: '/login', 
  name: 'login', 
  component: () => import('../views/login.vue')
}, {
  path: '/denied', 
  name: 'denied', 
  component: () => import('../views/denied.vue')
}]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

// 添加路由前置守卫
router.beforeEach((to, from, next) => {
  const store = userPermissionsStore()
  // 获取当前登录状态及用户角色
  const { isLogin, roles } = storeToRefs(store)
  // 判断该路由是否需要登录权限
  if (to.meta.requireAuth) {
    // 如果需要,则校验用户是否已经登录
    if (isLogin.value) {
      // 判断当前用户是否有访问该路由的权限
      if (to.meta.roles.includes(roles.value)) {
        next() // 用户有访问权限,直接进入页面
      } else {
        next('/denied') // 跳转到其他页面
      }
    } else {
      // 如果用户未登录,则跳转到登录页面
      next('/login')
    }
  } else {
    next() // 如果不需要登录权限,直接进入页面
  }
});

export default router


三、组件层面控制

1、使用自定义指令

可以利用 Vue 的指令来控制组件的显示和隐藏。例如,可以为每个组件设置一个权限属性,然后在指令中判断当前用户是否有访问该组件的权限,如果有,则显示组件,否则隐藏组件。利用 v-if 指令来判断当前用户是否有访问该组件的权限,并根据权限设置组件的显示和隐藏。

<template> 
    <el-button type="success" plain v-permission="'sys:user:add'">添加用户1</el-button>
</template> 

<script setup> 
import { userPermissionsStore } from '@/store/index'
import { storeToRefs } from 'pinia'
const store = userPermissionsStore()
const { userPermissions } = storeToRefs(store)

const vPermission = {
  mounted(el, binding) {
    const requiredPermission = binding.value;
    if (!userPermissions.value.includes(requiredPermission)) {
      el.style.display = 'none';
    }
  }
} 
</script>
2、使用方法控制
<template> 
     <el-button type="primary" plain v-if="hasPermission('sys:user:add')">添加用户1</el-button>
</template> 

<script setup> 
import { userPermissionsStore } from '@/store/index'
import { storeToRefs } from 'pinia'
const store = userPermissionsStore()
const { userPermissions } = storeToRefs(store)

const hasPermission = (permission) => {
  return userPermissions.value.includes(permission);
}
</script>
3、封装一个权限控制组件来实现组件层面控制权限
3.1、组件页面 Authority.vue
<template>
    <slot v-if="showSlot" :userPermissions="userPermissions"></slot>
</template>

<script setup>
import { computed } from 'vue';

import { storeToRefs } from 'pinia'
import { userPermissionsStore } from '@/store/index'
const store = userPermissionsStore()
// 为了从 store 中提取属性时保持其响应性,你需要使用 storeToRefs()
const { userPermissions } = storeToRefs(store)

const props = defineProps({
  // 需要的权限
    permissions: {
        type: [String, Array]
    }
});

const showSlot = computed(() => {
    if (!props.permissions) {
        return true
    }
    if (!userPermissions.value) {
        return false
    }
    if (Array.isArray(props.permissions)) {
        return props.permissions.every(p => {
            return userPermissions.value.includes(p)
        })
    } else {
        return userPermissions.value.includes(props.permissions)
    }
})

</script>

<style scoped>
</style>
3.2、使用页面 app.vue
<template>
  <el-select v-model="value" placeholder="请选择" @change="change" style="width: 300px; margin: 20px 0;">
    <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
  </el-select>

  <div>
    <!-- 这里可以根据返回的权限,做自己想判断的事情 -->
    <Authority>
      <template #default="{ userPermissions }">
        <el-button :disabled="!userPermissions.includes('sys:user:delete')" type="primary">禁用用户1</el-button>
      </template>
    </Authority>
    <!-- 传入组件所需要的权限 -->
    <Authority permissions="sys:user:view">
      <el-button>查询用户2</el-button>
    </Authority>
    <Authority permissions="sys:user:update">
      <el-button type="success">修改用户3</el-button>
    </Authority>
    <Authority permissions="sys:user:delete">
      <el-button type="info">删除用户4</el-button>
    </Authority>
    <Authority permissions="sys:user:add">
      <el-button type="warning">添加用户5</el-button>
    </Authority>
    <Authority :permissions="['sys:user:update', 'sys:user:delete']">
      <el-button type="danger">禁用用户6</el-button>
    </Authority>
  </div>
</template>

<script setup>
import Authority from '@/components/Authority.vue'
import { ref } from 'vue'

const value = ref('')
const options = [{
  value: '0',
  label: 'admin',
  permissions: ["sys:user:view", "sys:user:update", "sys:user:delete", "sys:user:add"]
}, {
  value: '1',
  label: 'editor',
  permissions: ["sys:user:view", "sys:user:update", "sys:user:add"]
}, {
  value: '2',
  label: 'guest',
  permissions: ["sys:user:view"]
}]

import { userPermissionsStore } from '@/store/index'
const store = userPermissionsStore()
// 作为 action 的 setUserPermissions 可以直接解构
const { setUserPermissions } = store
const change = (e) => {
  setUserPermissions(options[e].permissions)
}

</script>

<style scoped>
</style>
3.3、效果预览

这里使用el-select下拉框切换用户角色,存储pinia,是为了演示不同用户,不同的角色权限切换效果。

目录
相关文章
|
2月前
|
人工智能
歌词结构的巧妙安排:写歌词的方法与技巧解析,妙笔生词AI智能写歌词软件
歌词创作是一门艺术,关键在于巧妙的结构安排。开头需迅速吸引听众,主体部分要坚实且富有逻辑,结尾则应留下深刻印象。《妙笔生词智能写歌词软件》提供多种 AI 功能,帮助创作者找到灵感,优化歌词结构,写出打动人心的作品。
|
2月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
46 3
|
2月前
|
人工智能
写歌词的技巧和方法全解析:开启你的音乐创作之旅,妙笔生词智能写歌词软件
怀揣音乐梦想,渴望用歌词抒发情感?掌握关键技巧,你也能踏上创作之旅。灵感来自生活点滴,主题明确,语言简洁,韵律和谐。借助“妙笔生词智能写歌词软件”,AI辅助创作,轻松写出动人歌词,实现音乐梦想。
|
20天前
|
JSON PHP 数据格式
PHP解析配置文件的常用方法
INI文件是最常见的配置文件格式之一。
|
26天前
|
机器学习/深度学习 人工智能 安全
TPAMI:安全强化学习方法、理论与应用综述,慕工大、同济、伯克利等深度解析
【10月更文挑战第27天】强化学习(RL)在实际应用中展现出巨大潜力,但其安全性问题日益凸显。为此,安全强化学习(SRL)应运而生。近日,来自慕尼黑工业大学、同济大学和加州大学伯克利分校的研究人员在《IEEE模式分析与机器智能汇刊》上发表了一篇综述论文,系统介绍了SRL的方法、理论和应用。SRL主要面临安全性定义模糊、探索与利用平衡以及鲁棒性与可靠性等挑战。研究人员提出了基于约束、基于风险和基于监督学习等多种方法来应对这些挑战。
52 2
|
8天前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
37 0
|
1月前
|
前端开发 JavaScript 开发者
揭秘前端高手的秘密武器:深度解析递归组件与动态组件的奥妙,让你代码效率翻倍!
【10月更文挑战第23天】在Web开发中,组件化已成为主流。本文深入探讨了递归组件与动态组件的概念、应用及实现方式。递归组件通过在组件内部调用自身,适用于处理层级结构数据,如菜单和树形控件。动态组件则根据数据变化动态切换组件显示,适用于不同业务逻辑下的组件展示。通过示例,展示了这两种组件的实现方法及其在实际开发中的应用价值。
34 1
|
2月前
|
安全 Java
Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧
【10月更文挑战第20天】Java多线程通信新解:本文通过生产者-消费者模型案例,深入解析wait()、notify()、notifyAll()方法的实用技巧,包括避免在循环外调用wait()、优先使用notifyAll()、确保线程安全及处理InterruptedException等,帮助读者更好地掌握这些方法的应用。
19 1
|
2月前
|
SQL 监控 数据库
SQL语句是否都需要解析及其相关技巧和方法
在数据库管理中,SQL(结构化查询语言)语句的使用无处不在,它们负责数据的查询、插入、更新和删除等操作
|
26天前
|
机器学习/深度学习 自然语言处理 数据管理
GraphRAG核心组件解析:图结构与检索增强生成
【10月更文挑战第28天】在当今数据科学领域,自然语言处理(NLP)和图数据管理技术的发展日新月异。GraphRAG(Graph Retrieval-Augmented Generation)作为一种结合了图结构和检索增强生成的创新方法,已经在多个应用场景中展现出巨大的潜力。作为一名数据科学家,我对GraphRAG的核心组件进行了深入研究,并在此分享我的理解和实践经验。
51 0