后台管理系统 – 权限设计

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

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

一、前言

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

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

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

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

(具体内容尽量做到和技术框架无关,无论是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)
上一篇 2022年7月16日 上午10:36
下一篇 2022年7月16日 上午10:36


相关推荐

  • java查看环境变量的命令_java环境变量配置好了

    java查看环境变量的命令_java环境变量配置好了Linux是一个多用户的操作系统。每个用户登录系统后,都会有一个专用的运行环境。通常每个用户默认的环境都是相同的,这个默认环境实际上就是一组环境变量的定义。在Windows下,查看环境变量的命令是:set,这个命令会输出系统当前的环境变量。Linux下Linux查看环境变量准确的说是REDHAT下应该如何查看呢,命令是:export如果你想查看某一个名称的环境变量,命令是:echo$环境变量名,…

    2022年10月1日
    2
  • Oracle 回滚详解(rollback)

    Oracle 回滚详解(rollback)文章目录 1 概述 2 语法 2 1rollback2 2rollbacktos 3rollbackwor string 3 扩展 3 1rollback 官方解释 1 概述 1 rollback 是什么 1 回滚 2 事务管理语句之一 2 rollback 的作用 1 撤销 undo 当前的事务 2 与它相反的是 commit 提交当前的事务 2 语法可选子句解释 关键字解释备注

    2026年3月19日
    1
  • [Linux] 非root安装Lefse软件及其数据分析「建议收藏」

    [Linux] 非root安装Lefse软件及其数据分析「建议收藏」说明Lefse软件是宏组学物种研究常用软件,一般大家用在线版本即可。但要搭建在Linux集群环境中有点烦,记录一下折腾过程。安装这个软件是python2写的,因此假设我已经安装好了较高版本的python2以及pip等工具,在此基础上来安装lefse。lefse下载地址:https://bitbucket.org/nsegata/lefse/src/default/。这个网站有丰富…

    2022年5月26日
    29
  • paloalto防火墙内存使用率高

    paloalto防火墙内存使用率高

    2021年7月4日
    82
  • Java异常类型及处理

    Java异常类型及处理前言:Java异常,大家都很熟悉。但是对于具体怎么分类的,JVM对其怎么处理的,代码中怎么处理的,应该怎么使用,底层怎么实现的等等,可能就会有些不是那么清晰。本文基于此详细捋一下异常类型,实现以及使用时应怎么注意。一、异常实现及分类1.先看下异常类的结构图上图可以简单展示一下异常类实现结构图,当然上图不是所有的异常,用户自己也可以自定义异常实现。上图已经足够帮我们解释和理解异常…

    2022年5月19日
    39
  • quartus ii 12.0安装教程_系统安装教程

    quartus ii 12.0安装教程_系统安装教程安装前先关闭杀毒软件和360卫士,注意安装路径不能有中文,安装包路径也不要有中文。1.鼠标右击【QuartusII12.0】压缩包选择【解压到QuartusII12.0】。2.双击打开解压后的【QuartusII12.0】文件夹。3.双击打开【Quartus】文件夹。4.鼠标右击【12.0_178_quartus_windows.exe】选择【以管理员身份运行】。5.点击【Install】。6.解压中。7.勾选【AllowA…

    2022年10月8日
    3

发表回复

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

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