广告
返回顶部
首页 > 资讯 > 精选 >React中的权限组件设计问题怎么解决
  • 715
分享到

React中的权限组件设计问题怎么解决

2023-07-02 17:07:21 715人浏览 安东尼
摘要

这篇“React中的权限组件设计问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React中的权限组件设计问题怎么

这篇“React中的权限组件设计问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React中的权限组件设计问题怎么解决”文章吧。

背景

权限管理是中后台系统中常见的需求之一。之前做过基于 Vue 的后台管理系统权限控制,基本思路就是在一些路由钩子里做权限比对和拦截处理。

最近维护的一个后台系统需要加入权限管理控制,这次技术栈是React,我刚开始是在网上搜索一些React路由权限控制,但是没找到比较好的方案或思路。

这时想到ant design pro内部实现过权限管理,因此就专门花时间翻阅了一波源码,并在此基础上逐渐完成了这次的权限管理。

整个过程也是遇到了很多问题,本文主要来做一下此次改造工作的总结。

原代码基于 react 16.x、dva 2.4.1 实现,所以本文是参考了ant-design-pro v1内部对权限管理的实现

所谓的权限控制是什么?

一般后台管理系统的权限涉及到两种:

  • 资源权限

  • 数据权限

资源权限一般指菜单、页面、按钮等的可见权限。

数据权限一般指对于不同用户,同一页面上看到的数据不同。

本文主要是来探讨一下资源权限,也就是前端权限控制。这又分为了两部分:

  • 侧边栏菜单

  • 路由权限

在很多人的理解中,前端权限控制就是左侧菜单的可见与否,其实这是不对的。举一个例子,假设用户guest没有路由/setting的访问权限,但是他知道/setting的完整路径,直接通过输入路径的方式访问,此时仍然是可以访问的。这显然是不合理的。这部分其实就属于路由层面的权限控制。

实现思路

关于前端权限控制一般有两种方案:

  • 前端固定路由表和权限配置,由后端提供用户权限标识

  • 后端提供权限和路由信息结构接口,动态生成权限和菜单

我们这里采用的是第一种方案,服务只下发当前用户拥有的角色就可以了,路由表和权限的处理统一在前端处理。

整体实现思路也比较简单:现有权限(currentAuthority)和准入权限(authority)做比较,如果匹配则渲染和准入权限匹配的组件,否则渲染无权限组件(403 页面)

React中的权限组件设计问题怎么解决

路由权限

既然是路由相关的权限控制,我们免不了先看一下当前的路由表:

{    "name": "活动列表",    "path": "/activity-mgmt/list",    "key": "/activity-mgmt/list",    "exact": true,    "authority": [        "admin"    ],    "component": ƒ LoadableComponent(props),    "inherited": false,    "hideInBreadcrumb": false},{    "name": "优惠券管理",    "path": "/coupon-mgmt/coupon-rule-bplist",    "key": "/coupon-mgmt/coupon-rule-bplist",    "exact": true,    "authority": [        "admin",        "coupon"    ],    "component": ƒ LoadableComponent(props),    "inherited": true,    "hideInBreadcrumb": false},{    "name": "营销录入系统",    "path": "/marketRule-manage",    "key": "/marketRule-manage",    "exact": true,    "component": ƒ LoadableComponent(props),    "inherited": true,    "hideInBreadcrumb": false}

这份路由表其实是我从控制台 copy 过来的,内部做了很多的转换处理,但最终生成的就是上面这个对象。

这里每一级菜单都加了一个authority字段来标识允许访问的角色。component代表路由对应的组件:

import React, { createElement } from "react"import Loadable from "react-loadable""/activity-mgmt/list": {    component: dynamicWrapper(app, ["activityMgmt"], () => import("../routes/activity-mgmt/list"))},// 动态引用组件并注册modelconst dynamicWrapper = (app, models, component) => {  // reGISter models  models.forEach(model => {    if (modelNotExisted(app, model)) {      // eslint-disable-next-line      app.model(require(`../models/${model}`).default)    }  })  // () => require('module')  // transfORMed by babel-plugin-dynamic-import-node-sync  // 需要将routerData塞到props中  if (component.toString().indexOf(".then(") < 0) {    return props => {      return createElement(component().default, {        ...props,        routerData: getRouterDataCache(app)      })    }  }  // () => import('module')  return Loadable({    loader: () => {      return component().then(raw => {        const Component = raw.default || raw        return props =>          createElement(Component, {            ...props,            routerData: getRouterDataCache(app)          })      })    },    // 全局loading    loading: () => {      return (        <div          style={{            display: "flex",            justifyContent: "center",            alignItems: "center"          }}        >          <Spin size="large" className="global-spin" />        </div>      )    }  })}

有了路由表这份基础数据,下面就让我们来看下如何通过一步步的改造给原有系统注入权限。

先从src/router.js这个入口开始着手:

// 原src/router.jsimport dynamic from "dva/dynamic"import { Redirect, Route, routerRedux, Switch } from "dva/router"import PropTypes from "prop-types"import React from "react"import NoMatch from "./components/no-match"import App from "./routes/app"const { ConnectedRouter } = routerReduxconst RouterConfig = ({ history, app }) => {  const routes = [    {      path: "activity-management",      models: () => [import("@/models/activityManagement")],      component: () => import("./routes/activity-mgmt")    },    {      path: "coupon-management",      models: () => [import("@/models/couponManagement")],      component: () => import("./routes/coupon-mgmt")    },    {      path: "order-management",      models: () => [import("@/models/orderManagement")],      component: () => import("./routes/order-maint")    },    {      path: "merchant-management",      models: () => [import("@/models/merchantManagement")],      component: () => import("./routes/merchant-mgmt")    }    // ...  ]  return (    <ConnectedRouter history={history}>      <App>        <Switch>          {routes.map(({ path, ...dynamics }, key) => (            <Route              key={key}              path={`/${path}`}              component={dynamic({                app,                ...dynamics              })}            />          ))}          <Route component={NoMatch} />        </Switch>      </App>    </ConnectedRouter>  )}RouterConfig.propTypes = {  history: PropTypes.object,  app: PropTypes.object}export default RouterConfig

这是一个非常常规的路由配置,既然要加入权限,比较合适的方式就是包一个高阶组件AuthorizedRoute。然后router.js就可以更替为:

function RouterConfig({ history, app }) {  const routerData = getRouterData(app)  const BasicLayout = routerData["/"].component  return (    <ConnectedRouter history={history}>      <Switch>        <AuthorizedRoute path="/" render={props => <BasicLayout {...props} />} />      </Switch>    </ConnectedRouter>  )}

来看下AuthorizedRoute的大致实现:

const AuthorizedRoute = ({  component: Component,  authority,  redirectPath,  {...rest}}) => {  if (authority === currentAuthority) {    return (      <Route      {...rest}      render={props => <Component {...props} />} />    )  } else {    return (      <Route {...rest} render={() =>        <Redirect to={redirectPath} />      } />    )  }}

我们看一下这个组件有什么问题:页面可能允许多个角色访问,用户拥有的角色也可能是多个(可能是字符串,也可呢是数组)。

直接在组件中判断显然不太合适,我们把这部分逻辑抽离出来:

const checkPermissions = (authority, currentAuthority, target, Exception) => {  console.log("checkPermissions -----> authority", authority)  console.log("currentAuthority", currentAuthority)  console.log("target", target)  console.log("Exception", Exception)  // 没有判定权限.默认查看所有  // Retirement authority, return target;  if (!authority) {    return target  }  // 数组处理  if (Array.isArray(authority)) {    // 该菜单可由多个角色访问    if (authority.indexOf(currentAuthority) >= 0) {      return target    }    // 当前用户同时拥有多个角色    if (Array.isArray(currentAuthority)) {      for (let i = 0; i < currentAuthority.length; i += 1) {        const element = currentAuthority[i]        // 菜单访问需要的角色权限 < ------ > 当前用户拥有的角色        if (authority.indexOf(element) >= 0) {          return target        }      }    }    return Exception  }  // string 处理  if (typeof authority === "string") {    if (authority === currentAuthority) {      return target    }    if (Array.isArray(currentAuthority)) {      for (let i = 0; i < currentAuthority.length; i += 1) {        const element = currentAuthority[i]        if (authority.indexOf(element) >= 0) {          return target        }      }    }    return Exception  }  throw new Error("unsupported parameters")}const check = (authority, target, Exception) => {  return checkPermissions(authority, CURRENT, target, Exception)}

首先如果路由表中没有authority字段默认都可以访问。

接着分别对authority为字符串和数组的情况做了处理,其实就是简单的查找匹配,匹配到了就可以访问,匹配不到就返回Exception,也就是我们自定义的异常页面。

有一个点一直没有提:用户当前角色权限 currentAuthority 如何获取?这个是在页面初始化时从接口读取,然后存到 store

有了这块逻辑,我们对刚刚的AuthorizedRoute做一下改造。首先抽象一个Authorized组件,对权限校验逻辑做一下封装:

import React from "react"import CheckPermissions from "./CheckPermissions"class Authorized extends React.Component {  render() {    const { children, authority, noMatch = null } = this.props    const childrenRender = typeof children === "undefined" ? null : children    return CheckPermissions(authority, childrenRender, noMatch)  }}export default Authorized

接着AuthorizedRoute可直接使用Authorized组件:

import React from "react"import { Redirect, Route } from "react-router-dom"import Authorized from "./Authorized"class AuthorizedRoute extends React.Component {  render() {    const { component: Component, render, authority, redirectPath, ...rest } = this.props    return (      <Authorized        authority={authority}        noMatch={<Route {...rest} render={() => <Redirect to={{ pathname: redirectPath }} />} />}      >        <Route {...rest} render={props => (Component ? <Component {...props} /> : render(props))} />      </Authorized>    )  }}export default AuthorizedRoute

这里采用了render props的方式:如果提供了component props就用component渲染,否则使用render渲染。

菜单权限

菜单权限的处理相对就简单很多了,统一集成到SiderMenu组件处理:

export default class SiderMenu extends PureComponent {  constructor(props) {    super(props)  }    getSubMenuOrItem = item => {    if (item.children && item.children.some(child => child.name)) {      const childrenItems = this.getNavMenuItems(item.children)      // 当无子菜单时就不展示菜单      if (childrenItems && childrenItems.length > 0) {        return (          <SubMenu            title={              item.icon ? (                <span>                  {getIcon(item.icon)}                  <span>{item.name}</span>                </span>              ) : (                item.name              )            }            key={item.path}          >            {childrenItems}          </SubMenu>        )      }      return null    }    return <Menu.Item key={item.path}>{this.getMenuItemPath(item)}</Menu.Item>  }    getNavMenuItems = menusData => {    if (!menusData) {      return []    }    return menusData      .filter(item => item.name && !item.hideInMenu)      .map(item => {        // make dom        const ItemDom = this.getSubMenuOrItem(item)        return this.checkPermissionItem(item.authority, ItemDom)      })      .filter(item => item)  }    checkPermissionItem = (authority, ItemDom) => {    const { Authorized } = this.props    if (Authorized && Authorized.check) {      const { check } = Authorized      return check(authority, ItemDom)    }    return ItemDom  }  render() {    // ...    return      <Sider        trigger={null}        collapsible        collapsed={collapsed}        breakpoint="lg"        onCollapse={onCollapse}        className={siderClass}      >        <div className="loGo">          <Link to="/home" className="logo-link">            {!collapsed && <h2>冯言冯语</h2>}          </Link>        </div>        <Menu          key="Menu"          theme={theme}          mode={mode}          {...menuProps}          onOpenChange={this.handleOpenChange}          selectedKeys={selectedKeys}        >          {this.getNavMenuItems(menuData)}        </Menu>      </Sider>  }}

这里我只贴了一些核心代码,其中的checkPermissionItem就是实现菜单权限的关键。他同样用到了上文中的check方法来对当前菜单进行权限比对,如果没有权限就直接不展示当前菜单。

以上就是关于“React中的权限组件设计问题怎么解决”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注编程网精选频道。

--结束END--

本文标题: React中的权限组件设计问题怎么解决

本文链接: https://www.lsjlt.com/news/343438.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • React中的权限组件设计问题怎么解决
    这篇“React中的权限组件设计问题怎么解决”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“React中的权限组件设计问题怎么...
    99+
    2023-07-02
  • React中的权限组件设计问题小结
    目录背景所谓的权限控制是什么?实现思路路由权限菜单权限背景 权限管理是中后台系统中常见的需求之一。之前做过基于 Vue 的后台管理系统权限控制,基本思路就是在一些路由钩子里做权限比对...
    99+
    2022-11-13
  • react组件中过渡动画的问题解决
    目录一、是什么二、如何实现CSSTransitionSwitchTransitionTransitionGroup一、是什么 在日常开发中,页面切换时的转场动画是比较基础的一个场景 ...
    99+
    2022-11-13
  • 怎么解决React中的re-render问题
    这篇文章主要介绍“怎么解决React中的re-render问题”,在日常操作中,相信很多人在怎么解决React中的re-render问题问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么解决React中的re...
    99+
    2023-06-29
  • 怎么解决Win10管理员没有权限的问题
    本篇内容介绍了“怎么解决Win10管理员没有权限的问题”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!众所周知,管理员账号Administra...
    99+
    2023-06-07
  • 怎么解决React useEffect钩子带来的无限循环问题
    本篇内容主要讲解“怎么解决React useEffect钩子带来的无限循环问题”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么解决React useEffect钩子带来的无...
    99+
    2023-07-02
  • Element组件beforeUpload上传前限制失效问题怎么解决
    本篇内容介绍了“Element组件beforeUpload上传前限制失效问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Eleme...
    99+
    2023-07-05
  • 如何解决Go语言中的并发文件的权限管理问题?
    如何解决Go语言中的并发文件的权限管理问题?随着计算机科学的发展,现代编程语言越来越多地开始支持并发编程。并发编程可以充分利用多核处理器的优势,提高程序的执行效率。Go语言是一种支持并发编程的开发语言,并提供了丰富的并发编程库和工具。然而,...
    99+
    2023-10-22
    Go语言 解决方法 并发文件权限
  • k8s中各组件和kube apiserver通信时的认证和鉴权问题怎么解决
    这篇文章主要介绍“k8s中各组件和kube apiserver通信时的认证和鉴权问题怎么解决”,在日常操作中,相信很多人在k8s中各组件和kube apiserver通信时的认证和鉴权问题怎么解决问题上存在疑惑,小编查阅...
    99+
    2023-07-02
  • 怎么解决springboot+shiro+thymeleaf页面级元素的权限控制问题
    今天小编给大家分享一下怎么解决springboot+shiro+thymeleaf页面级元素的权限控制问题的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获...
    99+
    2023-06-29
  • Vue中父子组件间传值问题怎么解决
    本篇内容介绍了“Vue中父子组件间传值问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一.父组件向子组件传值父组件向子组件传值会用...
    99+
    2023-07-05
  • awk中的数组排序问题怎么解决
    在awk中,可以使用内置的sort函数对数组进行排序。sort函数的用法如下:```asort(array [, target])`...
    99+
    2023-09-16
    awk
  • element中form组件prop嵌套属性问题怎么解决
    本篇内容介绍了“element中form组件prop嵌套属性问题怎么解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Introductio...
    99+
    2023-06-29
  • 在Vue中使用dhtmlxGantt组件时遇到的问题怎么解决
    本文小编为大家详细介绍“在Vue中使用dhtmlxGantt组件时遇到的问题怎么解决”,内容详细,步骤清晰,细节处理妥当,希望这篇“在Vue中使用dhtmlxGantt组件时遇到的问题怎么解决”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢...
    99+
    2023-07-05
  • vue组件引用另一个组件出现组件不显示的问题怎么解决
    这篇文章主要介绍了vue组件引用另一个组件出现组件不显示的问题怎么解决的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue组件引用另一个组件出现组件不显示的问题怎么解决文章都会有所收获,下面我们一起来看看吧。组...
    99+
    2023-06-30
  • ACL中怎么设置用户访问指定文件/目录的权限
    这篇文章给大家介绍ACL中怎么设置用户访问指定文件/目录的权限,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Linux 对文件和目录有以下默认权限。文件 -> 644 -> -rw-r-r- (所有者有读写...
    99+
    2023-06-16
  • 服务器中怎么设置用户从网络访问计算机的权限
    在服务器中设置用户从网络访问计算机权限的方法:1.在管理工具中点击“本地安全策略”;2.选择"安全设置→本地策略"文件夹;3.右键拒绝从网络访问这台计算机,选择属性选项;4.点击"添加用户或组"按钮;...
    99+
    2022-10-18
  • DIV CSS设计中常见问题的解决办法是什么
    这篇文章将为大家详细讲解有关DIV CSS设计中常见问题的解决办法是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。作为前端开发人员,在日常的页面制作时,...
    99+
    2022-10-19
  • vant中field组件label属性两端对齐问题怎么解决
    这篇文章主要介绍“vant中field组件label属性两端对齐问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“vant中field组件label属性两端对齐问题怎么解决”文章能帮助大家解...
    99+
    2023-06-30
  • Vue3页面局部刷新组件的刷新问题怎么解决
    步入正题,解决今天的问题代码首先我们要对app.vue进行修改代码:<template> <div id="app"> <nav-View v-show="login&...
    99+
    2023-05-17
    Vue3
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作