后台管理系统 – 权限设计

后台管理系统 – 权限设计一、前言对于前端项目特别是中后台管理系统项目,权限设计是最复杂的点之一。一般来说权限设计需要后端来把关,毕竟相对来说前端是无法保证安全的,前端的代码和数据请求都可以伪造。而前端的权限设计更多是为了用户体验的考虑。前端保证体验,后端保证安全。由于前后端的开发差异和侧重点不同,在权限设计上也不一样。后端更多的是根据功能对象划分不同的权限模块,针对接口相应进行权限判断;而前端更多是针对页面路由进行模块划分,针对页面可访问进行判断。接下来将以后台管理系统为例,分享个人对前端权限设计的见解。(具体内容尽量做

大家好,又见面了,我是你们的朋友全栈君。

一、前言

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

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

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

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

(具体内容尽量做到和技术框架无关,无论是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, // 权限id
      hideMenu: 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.path
      if (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 } = meta
  if (!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,里面有完整的权限设计代码,供参考。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/160343.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • LARS(最小角回归)

    LARS(最小角回归)优缺点LARS是一个适用于高维数据的回归算法。优点: 特别适合于特征维度n远高于样本数m的情况。 算法的最坏计算复杂度和最小二乘法类似,但是其计算速度几乎和前向选择算法一样 可以产生分段线性结果的完整路径,这在模型的交叉验证中极为有用 缺点:由于LARS的迭代方向是根据目标的残差而定,所以该算法对样本的噪声极为敏感。…

    2022年4月20日
    39
  • Ubuntu安装gcc4.1.2

    Ubuntu安装gcc4.1.2安装之前,系统中必须要有cc或者gcc等编译器,并且是可用的,或者用环境变量CC指定系统上的编译器。如果系统上没有编译器,不能安装源代码形式的GCC4.1.2。如果是这种情况,可以在网上找一个与你系统相适应的如RPM等二进制形式的GCC软件包来安装使用。本文介绍的是以源代码形式提供的GCC软件包的安装过程,软件包本身和其安装过程同样适用于其它Linux和Unix系统。系统上原来的GCC编译…

    2022年7月24日
    12
  • Arduino TaskScheduler入门

    Arduino TaskScheduler入门#include<TaskScheduler.h>//回调函数声明,具体要执行的函数内容voidtask_1();//…voidtask_n();//计划任务声明,Task中要执行的函数名Taskt_end();//空任务,按需添加Taskt1(1000,10,&task_1);//任务名(间隔ms,执行次数,&执行函数)//…Tasktn(1000,TASK_FOREVER,&task_n);//TASK_ONCE,执.

    2022年8月31日
    4
  • G6流程图绘制

    G6流程图绘制为了能在线编辑流程 支持流程节点编辑等功能 支持人员等选择功能 支持流程图数据保存 利用阿里 G6 进行设计开发 整体效果图如下 支持放大缩小 节点移动 添加节点及边等 同时支持节点及边删除操作 流程图数据保存等工作 支持节点编辑 包括人员选择 图形选择 宽高编辑 背景色 边框色等信息编辑 支持边的编辑 边描述等 各种交互功能就不赘述了 页面代码如下 DOCTYPE YPE html head head html

    2025年7月25日
    2
  • Django(53)二次封装Response

    Django(53)二次封装Response前言有时候我们使用drf的Response,会发现默认返回的格式不太友好,每次我们都需要写入以下的格式returnResponse({"status":0,"

    2022年8月7日
    5
  • b站《史上最全unity3D教程》笔记1-04「建议收藏」

    b站《史上最全unity3D教程》笔记1-04「建议收藏」         

    2026年1月22日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号