一、前言

对于前端项目特别是中后台管理系统项目,权限设计是最复杂的点之一。

一般来说权限设计需要后端来把关,毕竟相对来说前端是无法保证安全的,前端的代码和数据请求都可以伪造。而前端的权限设计更多是为了用户体验的考虑。前端保证体验,后端保证安全。

由于前后端的开发差异和侧重点不同,在权限设计上也不一样。后端更多的是根据功能对象划分不同的权限模块,针对接口相应进行权限判断;而前端更多是针对页面路由进行模块划分,针对页面可访问进行判断。

接下来将以后台管理系统为例,分享个人对前端权限设计的见解。

(具体内容尽量做到和技术框架无关,无论是vue还是react都只是代码实现上的差异,主思路一致。不过话说vue的实现确实要比react简便很多,所以下述代码都以react为例)

二、页面级别

1、几种方式比较

先上几个常见的权限设计方式。

  • 方式一:由后端返回筛选后的路由配置,前端渲染

    • 描述:
      这种就是前端将所有路由配置数据给到后端存储,后端对根据用户权限对路由数据筛选后返回到前端,再由前端渲染。
    • 存在的问题:
      路由是前端使用的,为啥要存储到后端呢?要调整路由结构或者修改路由啥的都要找后端修改,前端开发不乐意,后端也不乐意,前后端分离的时代,这不是在倒退嘛。
  • 方式二:后端返回用户角色,前端根据角色做路由筛选

    • 描述:
      这种对方式一做了优化,方式一是后端根据权限筛选路由后返回前端,而现在是把筛选过程放在了前端,后端返回角色信息,前端遍历路由配置,根据角色筛选出有权限的路由渲染。
    • 存在的问题:
      但这种方式还有一个问题,就是角色的权限并非一成不变,一旦角色权限改了,前端的路由配置都要逐个排查修改,如果系统设计了动态修改角色权限的功能,那这种设计方式就没法用了。
  • 方式三:后端返回当前用户的所有权限id,前端根据权限id做路由筛选

    • 这也是本文推荐的方式,下面详细说明。

具体来说,就是对每一个页面路由都设置一个匹配的权限id(accessId),后端只需要把用户的所有权限id给到前端即可,不需要角色信息。

ps: 有些人可能对角色这点绕不过去,其实不管你的系统有没有角色这个概念,对于前端来说,角色只是一个对用户的一个称谓而已,在需要的时候展示这个称谓给用户界面。
一个角色可以有多个权限,然而前端不需要关心具体角色有哪些权限,前端需要的只是当前用户有哪些权限。
具体角色的权限数据只有在动态配置角色权限的页面才需要,实现上只需要遍历路由配置以一个tree树形组件展示即可,这种场景下也就是角色权限可能随时会变,前端就不应该以角色数据处理权限,而是应该以权限id来定,以不变应万变。
其实你也可以把每一个权限id都当成不同的角色理解,只不过这个角色就只有一个权限。

至于路由的权限id在哪里配置,这就看你项目的路由管理方案了,最好是对路由有一个统一管理,然后根据用户权限对路由做动态筛选,或者在路由访问时拦截判断。

2、导航菜单的处理

一般来说后台管理系统都会有个导航菜单,以侧边栏导航居多,对于用户来说这个也是所有页面的访问入口,所以导航菜单需要根据用户权限动态展示。

建议将所有路由配置信息存储在一个配置数组中,导航菜单就根据路由配置数组来动态生成,同时判断权限做筛选。

  • 对于 vue 来说,使用 vue-router 管理路由已经非常方便了;
  • 而 react 就有点麻烦,
    • 对于 react-router v5 及以下版本可以使用react-router-config来统一管理路由,
    • 对于 react-router v6 版本,安利一下个人封装的路由管理方案react-router-waiter(传送门)。

路由配置示例:

const routes = [{path: '/index',component: Index,meta: { // meta,自定义的数据都放这里面title: '首页', // 菜单标题accessId: 10000, // 权限idhideMenu: false, // 是否在侧边栏隐藏当前路由菜单noLogin: false, // 当前路由访问是否需要登录},},{path: '/nest',meta: {title: '多级菜单',},children: [{path: 'nest1',component: Nest1,meta: {title: '二级菜单',accessId: 10001,}},]},
]

导航菜单动态生成示例:

function getMenuList () {const getList = (routeList = [], prePath = '') => {let menuList = []// 遍历路由routeList.forEach(v => {v.meta = v.meta || {}// 排除不需要显示菜单的路由if (v.redirect || v.path === '*' || v.meta.hideMenu) {return}// 排除没有访问权限的路由if (!getIsCanAccess(v.meta.accessId)) {return}const currentPath = prePath + v.pathif (v.children) {// 有嵌套路由,递归添加菜单menuList.push((<SubMenu key={currentPath} title={v.meta.title}>{getList(v.children, currentPath + '/')}</SubMenu>))} else {// 无嵌套路由,菜单添加结束menuList.push((<ItemMenu key={currentPath}><Link to={currentPath}>{v.meta.title}</Link></ItemMenu>))}})return menuList}return getList(routes)
}

3、路由访问控制

导航菜单动态生成一定程度上限制了用户访问无权限的路由,但还不够,用户如果跳转一个没有权限的路由,或者在地址栏手动输入没有权限的路由网址,也是能访问页面,这就需要处理。

一般用户的权限信息都是从接口异步获取,所以我们需要在用户打开项目进入页面之前先请求接口拿到权限信息,然后再做后续页面的展示,这样才能保证在用户手动输入url场景下能有效地进行权限判断和路由拦截。

两种方式:

  • 1、简单的,获取权限信息 - 筛选路由配置数据 - 渲染路由。即拿到权限信息后就对路由配置数据做个过滤,只保留有权限的路由数据,再渲染路由,让用户访问无权限的路由时展示404页面。
  • 2、复杂点,获取权限信息 - 渲染路由 - 路由拦截处理。即拿到权限信息后直接渲染完整路由数据,然后通过路由的导航守卫做判断拦截,这样可以控制用户访问无权限的路由时展示403页面及更多提示信息,自定义性更强。

渲染路由前的控制,在入口组件App.vue或App.js里来写,代码示例:

import { HashRouter } from 'react-router-dom'
import RouterWaiter from 'react-router-waiter'export default function App () {const [isRender, setIsRender] = useState(false)useEffect(() => {// 解析url,获取path路由const path = getRoutePath()// 排除登录页等不需要权限的路由if (['/login'].includes(path)) {setIsRender(true)} else {// 判断是否已获取到权限信息if (!store.isGotUserInfo) {api.getUserInfo().then(res => {const data = res.data || {}// 权限信息存储到store状态管理数据中store.setUserInfo(data) // 获取完权限信息,放开路由渲染setIsRender(true)})}}}, [])return (<HashRouter>{isRender ? <RouterWaiter /> : null}</HashRouter>)
}
  • vue的实现也简单,在App.vue里通过v-if绑定控制<router-view />即可。

4、路由拦截

这是对上述“路由访问控制”的方式2的补充说明。

要实现路由拦截,需要对每一个路由的访问都做前置判断。

  • 对于vue,有自带的路由全局导航守卫beforeEach,处理很方便。
  • 而react没有,只能自行封装,再次安利一下react-router-waiter,对路由拦截也做了封装处理。

拦截判断的代码示例:

meta = meta || {} // 路由配置数据的meta字段
if (!meta.noLogin && store.isLogin) { // 登录判断const { accessId } = metaif (!store.isGotUserInfo) { // 是否已获取到用户(权限)信息api.getUserInfo().then(res => {const data = res.data || {}store.setUserInfo(data)// 无权限时拦截跳转403页面if (!getIsCanAccess(accessId)) {toPage403()}})} else {if (!getIsCanAccess(accessId)) {toPage403()}}
} else {// 未登录时拦截跳转登录页toPageLoin()
}

三、按钮级别

按钮级别,即页面中更细粒度的权限控制。

这个其实就很简单了,只需要控制相关的dom是否展示即可。

每一个需要控制的操作区域dom都给分配一个权限id,然后判断该用户是否具有该权限,控制该区域dom的显示隐藏。

后端也只需要把所有页面权限id和按钮级别的权限id都一箩筐给到前端就行。

  • vue里通过v-if绑定dom来处理就行,封装一个公共的方法来判断是否具有权限,也可以封装一个自定义指令来处理,以权限id为入参,使用更方便。
  • react里也差不多,通过jsx里if控制,同样可以封装个公共方法,也可以封装成一个公共组件处理。

代码示例:

return (<div>{getIsCanAccess('10008')? (<div>我是权限dom1</div>): null}<div>hello</div>{getIsCanAccess('10009')? (<div>我是权限dom2</div>): null}</div>
)

四、其他

基于此权限设计方案,个人搭建了一个react后台管理系统react-antd-mobx-admin,里面有完整的权限设计代码,供参考。

后台管理系统 - 权限设计相关推荐

  1. python开发企业管理平台_我的第一个python web开发框架(34)——后台管理系统权限设计...

    框架底层和接口终于改造完成了,小白再次找到老菜. 小白:老大,上次你对后台权限系统简单的讲了一下,我一点头绪都没有,现在有空完整的说一说吗? 老菜:说到权限系统,要讲明白真不容易,权限系统并不是越复杂 ...

  2. VUE后台管理系统权限管理

    VUE后台管理系统权限管理(面试路由守卫) 1.背景 后台管理系统中总会遇到权限分配的问题:这也是一道vue的很经典的面试问题 2.解决思路 权限管理无非前端或者后台解决 先说一下前端解决的思路:在设 ...

  3. P22-Vue3后台管理系统-权限管理之路由守卫判断⽤户登录状态

    P22-Vue3后台管理系统-权限管理之路由守卫判断⽤户登录状态 文章目录 P22-Vue3后台管理系统-权限管理之路由守卫判断⽤户登录状态 1.概述 2.mock接口返回token 2.1.mock ...

  4. SSM框架实现后台管理系统权限管理(用户、菜单、角色)

    文章目录 后台管理系统开发 一.数据库表结构设计 1.菜单表menu 2.用户表user 3.角色表role 4.角色权限表role_menu 5.用户角色表user_role 二.项目准备 1.创建 ...

  5. 06_04_SSM拉勾教育后台管理系统(权限模块\登录及动态菜单)

    拉勾教育后台管理系统(SSM)权限模块 权限概念介绍 权限:权利(能做的)和限制(不能做的),在权限范围内做好自己的事情,不该看的不看,不该做的不做 认证: 验证用户名密码是否正确的过程 授权: 对用 ...

  6. 【完善】微信餐厅点单小程序+后台管理系统的设计与实现(python实现)

    前言 本文为完善上一篇文章餐厅点单小程序+后台管理管理系统的设计与实现,旨在帮助有需要的小伙伴,更好的入门学习python3 django+vue开发的前后端分离框架.话不多说,开始我们的学习吧- 项 ...

  7. 后台管理系统权限管理详解

    权限管理 简述权限管理: 你可以在后台通过一个 tree 控件或者其它展现形式给每一个页面动态配置权限,之后将这份路由表存储到后端.当用户登录后得到roles,前端根据roles去向后端请求可访问的路 ...

  8. 浅聊下后台管理系统权限控制的实现思路

    总览: 总的思路来讲实现权限控制分为页面的权限以及页面按钮的权限,页面权限可以简单理解为哪些页面这个用户可以看见,哪些页面用户不能看见,按钮权限同理,哪些按钮用户可以看见,哪些按钮用户看不见,为什么不 ...

  9. zsy后台管理系统-架构设计

    Zsy框架总体架构设计 1.Mysql数据库,存储所有表的数据. 2.Zsy-基础项目(Zsy-Model,Zsy-Dao,Zsy-Service,Zsy-Web),基于SSM框架.项目功能包含基本的 ...

  10. elm_flask 项目日志 -- flask后台管理系统开发设计

    后台管理项目日志 项目初始化阶段 仓库使用 注册码云平台 创建工程仓库 创建自己的开发分支 clone/push/pull的操作 新建开发分支 能够在远程分支进行pull和push 项目搭建 搭建项目 ...

最新文章

  1. Nature子刊:精胺介导稻瘟病菌侵染水稻叶片的新机制!
  2. HBase应用快速学习
  3. 将spark默认日志log4j替换为logback
  4. IIS中WEB服务器的日志存放到SQL Server 2005中
  5. c#中Split分割字符串的几种方法
  6. 新型冠状病毒肺炎国内分省分日期从1.16起的全部数据爬取与整理代码(附下载)
  7. 【java】java 并发编程 CopyOnWriteArrayList
  8. 通讯信息诈骗背后的个人信息安全之痛
  9. 这两天在学习微信小程序,不可避免的又要用到一些图标,通过问朋友解决了这个事情 想分享给你们一下 希望对你们有用...
  10. ORACLE--Connect By、Level、Start With的使用(Hierarchical query-层次查询)
  11. 多用户访问SSAS cube权限设置
  12. vue 动态设置浏览器标题
  13. WikiExtractor.py(维基百科抽取器)
  14. 移动应用的引导模式设计
  15. K8S报错network is not ready: container runtime network not ready
  16. android和ios手机换行,ios label中文与数字混合导致换行解决
  17. GitHub上有哪些优秀的爬虫项目?
  18. The C Programming Language 读书总结
  19. 【PyQt】Qt中QMainWindow, QWidget以及QDialog的区别和选择
  20. python中的mul函数_mul函数的具体用法

热门文章

  1. 旧电脑 软路由 openwrt 自定义安装ipk 操作记录
  2. 兼容性问题以及解决方案
  3. 一维非齐次热传导方程的紧致差分格式(附Matlab代码)
  4. matlab gui编程教程,matlab如何使用gui
  5. 百度初级认证考试知识点
  6. VCC,GND,VSS,VDD的理解
  7. 智慧校园网络机房建设解决方案2.0
  8. 大数据可视化工具在中国的发展历程
  9. 3D建模、处理软件及部分算法库简介
  10. C# 将Big5繁体转换简体GB2312的代码