前端Vue:权限管理,给角色分配权限

news2024/11/22 21:34:06

👉前端-Vue权限控制,菜单权限,按钮权限_一人创客的博客-CSDN博客

目录

介绍:

前端权限的概念:

前端权限的意义:

Vue权限管理的代码实现:

菜单

刷新界⾯菜单消失

标识⽤户名, ⽅便查看当前⽤户

退出登陆: 

界面:

1.判断当前是否登陆

2.控制是否可以访问角色界面 (不太理解)

按钮:

请求和响应:

请求控制 

响应控制

总结(重要):

菜单:

界面:

 按钮控制

请求和响应控制


介绍:

前端权限的概念:

        前端权限的控制本质上来说, 就是控制前端的 视图层的展示和前端所发送的请求。

        前端权限的实现必须要后端提供数据⽀持, 否则⽆法实现。返回的权限数据的结构,前后端需要沟通协商, 怎样的数据使⽤起来才最⽅便.

前端权限的意义:

        如果仅从能够修改服务器中数据库中的数据层⾯上讲,确实只在后端做控制就⾜够了, 那为什么越来越多的项⽬也进⾏了前端权限的控制, 主要有这⼏⽅⾯的好处:

        降低⾮法操作的可能性
        不怕贼偷就怕贼惦记, 在⻚⾯中展示出⼀个 就算点击了也最终会失败 的按钮, 势必会增加有⼼者
        ⾮法操作的可能性
        尽可能排除不必要请求,减轻服务器压⼒
        没必要的请求, 操作失败的请求, 不具备权限的请求, 应该压根就不需要发送, 请求少了, ⾃然也会减轻服务器的压⼒
        提⾼⽤户体验
        根据⽤户具备的权限为该⽤户展现⾃⼰权限范围内的内容,避免在界⾯上给⽤户带来困扰, 让⽤户专注于分内之事

Vue权限管理的代码实现:

菜单

        在登录请求中, 会得到权限数据, 当然, 这个需要后端返回数据的⽀持. 前端根据权限数据, 展示对应的菜单.点击菜单,才能查看相关的界⾯.

//查看登陆之后获取的数据
{

"data": {

"id": 500,

"rid": 0, "username": "admin",

"mobile": "13999999999",

"email": ["123999@qq.com"](mailto:123999@qq.com), "token": "Bearer

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjUwMCwicmlkIjowLCJpYXQiOjE1M TI1NDQyOTksImV4cCI6MTUxMjYzMDY5OX0.eGrsrvwHm- tPsO9r_pxHIQ5i5L1kX9RX444uwnRGaIM"

},

"rights": [{

"id": 125,

"authName": "⽤户管理",

"icon": "icon-user",

"children": [{

"id": 110,

"authName": "⽤户列表",

"path": "users",
"rights": ["view", "edit", "add", "delete"]
}]
}, {
"id": 103,
"authName": " ⻆ ⾊ 管 理 ", "icon": "icon-tijikongjian", "children": [{
"id": 111,
"authName": "⻆⾊列表",
"path": "roles",
"rights": ["view", "edit", "add", "delete"]
}]
}, {
"id": 101,
"authName": "商品管理",
"icon": "icon-shangpin",
"children": [{
"id": 104,
"authName": "商品列表",
"path": "goods",
"rights": ["view", "edit", "add", "delete"]
}, {
"id": 121,
"authName": "商品分类", "path": "categories",
"rights": ["view", "edit", "add", "delete"]
}]
}],
"meta": {
"msg": "登录成功", "status": 200
}
}

在这部分数据中, 除了该⽤户的基本信息之外, 还有两个字段很关键

        token,⽤于前端⽤户的状态保持

        rights:该⽤户具备的权限数据,⼀级权限就对应⼀级菜单,⼆级权限就对应⼆级菜单。根据rights中的数据, 动态渲染左侧菜单栏, 数据在Login.vue得到, 但是在Home.vue才使⽤, 所以可以把数据⽤vuex进⾏维护:

//vuex的代码:
export default new Vuex.Store({ 
    state: {
        rightList:[]
    },
    mutations: { 
        setRightList(state, data) {
            state.rightList = data
        }
    },
    actions: {

    },
    getters: {

    }
})


//login的代码:
login() {
    this.$refs.loginFormRef.validate(async valid => {
    ......
    this.$store.commit('setRightList', res.rights) 
    this.$message.success(' 登 录 成 功 ') 
    this.$router.push('/home')
    
    })
}

//home的代码
import { mapState } from 'vuex' 
computed: {
    ...mapState(['rightList'])
}
created() {
    this.activePath = window.sessionStorage.getItem('activePath') 
    this.menulist = this.rightList
},


刷新界⾯菜单消失

        因为菜单数据是登录之后才获取到的, 获取菜单数据之后,就存放在Vuex中 ,⼀旦刷新界⾯, Vuex中的数据会重新初始化, 所以会变成空的数组。 因此, 需要将权限数据存储在sessionStorage中, 并让其和Vuex中的数据保持同步:
     

//vuex的代码
export default new Vuex.Store({ 
state: {
    rightList:JSON.parse(sessionStorage.getItem('rightList')||'[]')
},
mutations: { 
    setRightList(state, data) {
       state.rightList = data                 
       sessionStorage.setItem('rightList',JSON.stringify(data))
    }
},
actions: {
},
getters: {
}
})

标识⽤户名, ⽅便查看当前⽤户

//vuex的代码
export default new Vuex.Store({ 
state: {
    //把数据转成对象
    rightList:JSON.parse(sessionStorage.getItem('rightList')||'[]'), 
    username: sessionStorage.getItem('username')
},
mutations: { 
    setRightList(state, data) {
        state.rightList = data         
        sessionStorage.setItem('rightList',JSON.stringify(data))
    },
    setUsername(state, data) { 
        state.username = data
        sessionStorage.setItem('username',data)
    }
},
actions: {
},
getters: {
}
})

//login的代码

login() {
    this.$refs.loginFormRef.validate(async valid => {
        ......
        this.$store.commit('setRightList', res.rights)         
        this.$store.commit('setUsername', res.data.username) 
        this.$message.success(' 登 录 成 功 ') this.$router.push('/home')
   })
}


//home的代码

computed: {
    ...mapState(['rightList','username'])
}
<el-button type="info" @click="logout">{{username}}退出</el-button>



退出登陆: 


logout() {
    sessionStorage.clear() 
    this.$router.push('/login') 
    //刷新当前页面
    window.location.reload()
},

界面:

1.判断当前是否登陆

        正常的逻辑是通过登录界⾯, 登录成功之后跳转到管理平台界⾯, 但是如果⽤户直接敲⼊管理平台的地址, 也是可以跳过登录的步骤.所以应该在某个时机判断⽤户是否登录:

//如何判断是否登录
sessionStorage.setItem('token', res.data.token)


//判断时机|路由导航守卫
router.beforeEach((to, from, next) => { 
    if (to.path === '/login') {
        next()
    } else {
        const token = sessionStorage.getItem('token') 
        if(!token) {
            next('/login')
        } else {
            next()
            }
       }
})

2.控制是否可以访问角色界面 (不太理解)

        虽然菜单项已经被控制住了, 但是路由信息还是完整的存在于浏览器,正⽐如zhangsan这个⽤户并不具备⻆⾊这个菜单, 但是他如果⾃⼰在地址栏中敲⼊/roles的地址, 依然也可以访问⻆⾊界⾯:

//router.js
//登录成功之后动态添加
import Vue from 'vue'
import Router from 'vue-router'
import Login from '@/components/Login.vue' 
import Home from '@/components/Home.vue' 
import Welcome from '@/components/Welcome.vue'
import Users from '@/components/user/Users.vue' 
import Roles from '@/components/role/Roles.vue'
import GoodsCate from '@/components/goods/GoodsCate.vue' 
import GoodsList from '@/components/goods/GoodsList.vue' 
import NotFound from '@/components/NotFound.vue'
import store from '@/store' Vue.use(Router)

const userRule = { path: '/users', component: Users } 
const roleRule = { path: '/roles', component: Roles } 
const goodsRule = { path: '/goods', component: GoodsList }
const categoryRule = { path: '/categories', component: GoodsCate }
const ruleMapping = { 
    'users': userRule, 
    'roles': roleRule, 
    'goods': goodsRule, 
    'categories': categoryRule
    }


const router = new Router({ routes: [

{

path: '/', redirect: '/home'

},

{

path: '/login', component: Login

},

{

path: '/home', component: Home, redirect: '/welcome', children: [

{ path: '/welcome', component: Welcome },

// { path: '/users', component: Users },
// { path: '/roles', component: Roles },
// { path: '/goods', component: GoodsList },
// { path: '/categories', component: GoodsCate }
]
},
{
path: '*', component: NotFound
}

]
})

router.beforeEach((to, from, next) => {
    if (to.path === '/login') {
             next()
    } else {
        const token = sessionStorage.getItem('token') 
        if(!token) {
            next('/login')
        } else {
            next()
        }
   }
})

export function initDynamicRoutes() {
    const currentRoutes = router.options.routes 
    const rightList = store.state.rightList 
    
    rightList.forEach(item => {
        item.children.forEach(item => {             
        currentRoutes[2].children.push(ruleMapping[item.path])
        })
    })
    router.addRoutes(currentRoutes)
}

export default router
//login.vue
import { initDynamicRoutes } from '@/router.js' 
login() {
    this.$refs.loginFormRef.validate(async valid => { 
    if (!valid) return
    const { data: res } = await this.$http.post('login', this.loginForm)
    if (res.meta.status !== 200) return this.$message.error('登录失败!')
    this.$store.commit('setRightList', res.rights)
    this.$store.commit('setUsername', res.data.username)         
    sessionStorage.setItem('token', res.data.token) 
    initDynamicRoutes()
    this.$message.success('登录成功')
    this.$router.push('/home')
})
}

//app.vue
import { initDynamicRoutes } from '@/router.js' 
export default {
    name: 'app', created() {
    initDynamicRoutes()
}
}

按钮:

        虽然⽤户可以看到某些界⾯了, 但是这个界⾯的⼀些按钮,该⽤户可能是没有权限的.因此, 我们需要对组件中的⼀些按钮进⾏控制. ⽤户不具备权限的按钮就隐藏或者禁⽤, ⽽在这块中, 可以把该逻辑放到⾃定义指令中

//permission.js

import Vue from 'vue'
import router from '@/router.js' 
Vue.directive('permission', {
    inserted: function(el, binding){ const action = binding.value.action
    const currentRight = router.currentRoute.meta 
    if(currentRight) {
        if(currentRight.indexOf(action) == -1) {
            // 不具备权限
            const type = binding.value.effect 
            if(type === 'disabled') {
                 el.disabled = true el.classList.add('is-disabled')
            } else {
                 el.parentNode.removeChild(el)
                }
            }
        }
    }
})


//main.js
import './utils/permission.js'


//router.js

export function initDynamicRoutes() {
    const currentRoutes = router.options.routes 
    const rightList = store.state.rightList 
    rightList.forEach(item => {
        item.children.forEach(item => {
            const itemRule = ruleMapping[item.path] 
            itemRule.meta = item.rights currentRoutes[2].children.push(itemRule)
            })
        })
    router.addRoutes(currentRoutes)
}

使用指令:


v-permission="{action:'add'}"
v-permission="{action:'delete', effect:'disabled'}"

请求和响应:

        如果⽤户通过⾮常规操作, ⽐如通过浏览器调试⼯具将某些禁⽤的按钮变成启⽤状态, 此时发的请求, 也应该被前端所拦截

请求控制 

  • 除了登录请求都得要带上token, 这样服务器才可以鉴别你的身份

axios.interceptors.request.use(function(req){ 
    const currentUrl = req.url
    if(currentUrl !== 'login') {
        req.headers.Authorization = sessionStorage.getItem('token')
    }
    return req
})
  • 如果发出了⾮权限内的请求, 应该直接在前端访问内组织,虽然这个请求发到服务器也会被拒绝

import axios from 'axios' import Vue from 'vue'
import router from '../router'
// 配置请求的跟路径, ⽬前⽤mock模拟数据, 所以暂时把这⼀项注释起来
// axios.defaults.baseURL = 'http://127.0.0.1:8888/api/private/v1/' const actionMapping = {
get: 'view',
post: 'add',
put: 'edit', delete: 'delete'
}
axios.interceptors.request.use(function(req){ const currentUrl = req.url
if(currentUrl !== 'login') {
req.headers.Authorization = sessionStorage.getItem('token')
// 当前模块中具备的权限
// 查看	get请求
// 增加	post请求
// 修改	put请求
// 删除	delete请求
const method = req.method
// 根据请求, 得到是哪种操作
const action = actionMapping[method]
// 判断action是否存在当前路由的权限中
const rights = router.currentRoute.meta if(rights && rights.indexOf(action) == -1) {
// 没有权限
alert('没有权限')
return Promise.reject(new Error('没有权限'))
}
}
return req
})
axios.interceptors.response.use(function(res){ return res
})
Vue.prototype.$http = axios

响应控制

  • 得到了服务器返回的状态码401, 代表token超时或者被篡改了, 此时应该强制跳转到登录界⾯
axios.interceptors.response.use(function(res){ 
    if (res.data.meta.status === 401) {
        router.push('/login') 
        sessionStorage.clear() 
        window.location.reload()
    }
    return res
})

总结(重要):

菜单:

  • 权限的数据需要在多组件之间共享, 因此采⽤vuex

  • 防⽌刷新界⾯,权限数据丢失, 所以需要存储在sessionStorage, 并且要保证两者的同步

界面:

  • 路由的导航守卫可以防⽌跳过登录界⾯

  • 动态路由可以让不具备权限的界⾯的路由规则压根就不存在

 按钮控制

  • 路由规则中可以增加路由元数据meta

  • 通过路由对象可以得到当前的路由规则,以及存储在此规则中的meta数据

  • ⾃定义指令可以很⽅便的实现按钮控制

请求和响应控制

  • 请求拦截器和响应拦截器的使⽤
  • 请求⽅式的约定restful

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/559347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-python_kali

Python与Kali Linux&#xff1a;SEO攻击的更有效方法 介绍 Python是一种强大且高度灵活的编程语言&#xff0c;也是许多安全专家、黑客和网络攻击者所喜欢的工具之一。Kali Linux是一个专注于安全审计、渗透测试和网络安全的Linux发行版&#xff0c;其中包含了许多流行的安全…

微服务开发系列 第四篇:分页查询

总概 A、技术栈 开发语言&#xff1a;Java 1.8数据库&#xff1a;MySQL、Redis、MongoDB、Elasticsearch微服务框架&#xff1a;Spring Cloud Alibaba微服务网关&#xff1a;Spring Cloud Gateway服务注册和配置中心&#xff1a;Nacos分布式事务&#xff1a;Seata链路追踪框架…

Chat GPT 教您如何发现和处理无效数据

Chat GPT 教您如何发现和处理无效数据 在进行数据管理时&#xff0c;无论是数据分析、数据挖掘还是机器学习项目&#xff0c;无效数据都可能对结果造成严重的影响。因此&#xff0c;发现和处理无效数据变得至关重要。本文将从如何处理无效数据的角度&#xff0c;详细探讨数据清…

数据结构与算法(五)

哈希表&#xff08;hash&#xff09; 什么是hash&#xff1f; 散列,是把任意长度的输入通过散列算法变换成固定长度输出&#xff0c;该输出的值就是散列值。这种转换是一种压缩映射。映射表达的是一一对应的关系&#xff0c;也就是说&#xff0c;散列值的空间通常会小于输入空…

[算法前沿]--014-DeepSpeed-Chat 模型训练实战<下>

文章目录 1.实战Step1:监督微调1.1 任务说明: 使用标定的数据对预训练模型进行微调评价与测试:2 实战Step2:Reward模型微调3.实战Step3:RLHF训练3.评价与测试4.QA参考1.实战Step1:监督微调 基础语言模型是指只在大规模文本语料中进行了预训练的模型,未经过指令和下游任务…

淘宝商品历史价格API接口 调用说明及功能介绍

淘宝商品历史价格API是一款可以帮助用户获取淘宝商品历史价格数据的接口。通过该接口&#xff0c;用户可以轻松地获取某个商品在过去一段时间中的价格趋势和波动情况&#xff0c;以便更好地了解该商品的市场走势和价值变化情况。 该API具备以下功能&#xff1a; 1. 支持多种查…

对于大流量请求的处理方案(NATNginx)

情况描述&#xff1a; 如图所示&#xff0c;厂家的A服务器&#xff0c;到客户的C服务器不通&#xff0c;需要我这边通过B服务器做一次流量转发。 由于&#xff0c;每次请求数据流都太大&#xff0c;怕HTTPS方式&#xff0c;会出现请求超时&#xff0c;断开连接。 解决方案&am…

什么是自动化测试框架?我们该如何搭建自动化测试框架?

无论是在自动化测试实践&#xff0c;还是日常交流中&#xff0c;经常听到一个词&#xff1a;框架。之前学习自动化测试的过程中&#xff0c;一直对“框架”这个词知其然不知其所以然。 最近看了很多自动化相关的资料&#xff0c;加上自己的一些实践&#xff0c;算是对“框架”…

Codeium:一个免费的、支持70多种编程语言的、可以与你对话的智能编程助手,让你从繁琐的代码中解放出来

摘要 Codeium&#xff1a;免费的人工智能代码加速工具&#xff0c;让编程变得更简单、更快、更有趣 如何使用Codeium来提高编程效率和质量&#xff1f;一篇文章教你掌握Codeium的三大功能&#xff1a;代码完成、聊天和搜索 Codeium vs GitHub Copilot&#xff1a;哪个更适合你…

Linux的软件生态与两个方面,客户端/Linux软件下载安装的认识,yum源/仓库(repo)与yum指令的本质,yum指令操作等

铺垫1&#xff1a;服务器属于硬件 服务器是一种计算机硬件设备&#xff0c;主要用于存储、管理和处理数据以及为其他计算机提供服务。服务器通常具有高性能的处理器、大容量的硬盘、大内存和高速网络连接等特点&#xff0c;可以提供各种服务&#xff0c;如网站托管、电子邮件服…

【教程】对视频平台授权时,加密机设备如何固定IP?

我们在此前的文章中也介绍过&#xff0c;我们的视频平台都是通过加密机、加密狗、激活码三种方式进行服务授权的&#xff0c;其中&#xff0c;加密机使用得较多。具体注意事项可以查看这篇文章&#xff1a;加密机授权注意事项汇总及解决方法。 加密机在使用时&#xff0c;需要在…

bat操作git(一键提交)

添加环境变量&#xff1a;D:\Git\Git\cmd 环境变量添加完毕后就可以直接在命令框使用git命令了 脚本实现 实现一键完成远程仓库的更新 echo off git add . git commit -m "daily push data-structure-and-algorithms" git push echo push respostory successful…

python包之matplotlib基础概念和代码详解

1 基础概念 Figure&#xff1a; 可以理解为 canvas(画布)&#xff0c;在画布上可以展示一个或多个Axes Axes&#xff1a;中文翻译为轴&#xff0c;但与数学中的概念不同&#xff0c;Axes可以理解为子画布&#xff0c;它属于Figure。也可以理解为它就是一个图形或绘制图形的区…

为什么有了IP地址,还需要MAC地址呢?

不知道大家有没有困惑&#xff1a;为什么有了IP地址&#xff0c;还需要MAC地址呢&#xff1f;他们之间到底有什么联系&#xff1f;又有什么区别&#xff1f;是不是有一个是多余的&#xff1f; 流言传到了“IP地址”和“MAC地址”的耳朵里&#xff0c;他俩也非常苦恼&#xff0c…

【Unity】 UI自适应案例

UI自适应案例 案例一:背包自动布局1. 创建背包面板2. 背包子项自动布局3. C#代码:动态添加子项到背包中案例二:文字自适应高度1. 创建文字面板2. 组件基本设置3. C#代码:动态更新文字并自适应高度案例一:背包自动布局 需求:动态添加背包组件,设定每行特定个数并自动匹配…

抖音seo矩阵系统源码开发(三)

抖音seo框架分析&#xff1a; 抖音SEO源码主要有两种框架&#xff1a; 一是基于爬虫的框架&#xff0c;通过爬取抖音平台的内容&#xff0c;提取关键词和标签等信息&#xff0c;再结合优化技巧&#xff0c;最终实现SEO效果的提升&#xff1b;二是基于粉丝互动和品牌策划的框架…

制作iOS越狱deb插件+dpkg命令行教程

iOS越狱deb插件的制作 dpkg命令行教程 deb安装包的制作 介绍 Cydia Sileo都是基于Debian开发的, 所以插件都是打包成.deb格式 deb包是Debian软件包格式&#xff0c;文件扩展名为.deb。是Debian系统(包含Debian和Ubuntu等)专属安装包格式。 deb包在Linux操作系统中类似于wi…

探索小程序容器在软件应用架构中的角色和优势

今年来&#xff0c;随着软件及开源技术的发展&#xff0c;软件应用架构的概念也随之流行起来。它提供了一种组织和设计软件系统的有效方法&#xff0c;具有许多优势和好处&#xff1a; 模块化和可维护性&#xff1a;软件应用架构将系统拆分为模块化的组件&#xff0c;每个组件负…

00后实在太卷了,测试用例写的比我还好,羞耻啊.....

经常看到无论是刚入职场的新人&#xff0c;还是工作了一段时间的老人&#xff0c;都会对编写测试用例感到困扰&#xff1f;例如&#xff1a; 如何编写测试用例&#xff1f; 作为一个测试新人&#xff0c;刚开始接触测试&#xff0c;对于怎么写测试用例很是头疼&#xff0c;无…

面了一个5年经验的测试工程师,自动化都不会也敢喊了16k,我也是醉了····

在深圳这家金融公司也待了几年&#xff0c;被别人面试过也面试过别人&#xff0c;大大小小的事情也见识不少&#xff0c;今天又是团面的一天&#xff0c; 一百多个人都聚集在一起&#xff0c;因为公司最近在谈项目出来面试就2个人&#xff0c;无奈又被叫到面试房间。 整个过程…