【郑州研发中心】react-router实现权限控制小记

简介: 使用react-router实现权限控制

1. 权限控制各端需要做的工作

  • 前端:
  • 菜单/按钮作为页面的入口,让用户直接操作就能进入到对应的页面,是需要做权限控制的;菜单通常是无权限直接就不展示,按钮分情况不展示或点击给出提示,结合具体的需求场景来实现
  • 路由权限控制:只控制菜单/按钮的不展示,并不完善,因为还可以通过输入URL来进入相应页面,而路由决定了能否进入当前页面,所以还需要前端对路由进行权限控制
  • 后端:后端相对前端来说,权限控制更为重要,因为可以不通过界面而是直接通过接口来获取数据,所以后端必须对接口访问权限进行控制;同时如果对接口进行了控制,返回无权限状态码,前端同样可以根据状态码来进行重定向至无权限界面。

2. 前端权限控制实现

1. 获取权限列表的时机

应当是在用户登录后,内容页面展示之前获取权限列表,这样在展示具体页面前就把路由和菜单展示控制好。

2. 控制菜单的显示

改造前的项目菜单为配置式,由后台返回,后期因为要结合4A平台实现权限控制,后台改造较为麻烦,改为前端本地固定写死,后端只返回权限列表,这样就需要本地配置一个菜单表,结合后台返回的权限列表,对菜单进行权限赋值,如果有权限的情况下,菜单展示,无权限则直接不展示。因为菜单通常都是嵌套的,所以这里主要的实现就递归:

// 使用递归菜单权限赋值,获取到有权限的菜单列表functiongetMenuAuthor(menuList, resources) {
menuList.forEach(item=> {
if (item.children) {
// 如果包含子项,递归getMenuAuthor(item.children, resources);
    } elseif (resources.includes(item.url)) {
// 权限赋值item.authority=true;
    }
  });
returnmenuList;
}

参考:

  • 菜单数据demo:
// 菜单列表constmenuList= [
  {
"key": "sub1",
"name": "Navigation One",
"children": [
      {
"key": "g1",
"name": "item1",
"icon": "comment",
"children": [
          {
"key": "1",
"name": "Option 1",
"icon": null,
"url": "/sub1/item1/option1",
"children": null          },
          {
"key": "2",
"name": "Option 2",
"icon": null,
"url": "/sub1/item1/option2",
"children": null          },
        ]
      },
      {
"key": "g2",
"name": "item2",
"icon": "setting",
"children": [
          {
"key": "1",
"name": "Option 1",
"icon": null,
"url": "/sub1/item2/option1",
"children": null          },
          {
"key": "2",
"name": "Option 2",
"icon": null,
"url": "/sub1/item2/option2",
"children": null          },
        ]
      }]
  },
  {
"key": "sub2",
"name": "Navigation Two",
"children": [
      {
"key": "g1",
"name": "item1",
"icon": "comment",
"children": [
          {
"key": "1",
"name": "Option 1",
"icon": null,
"url": "/sub2/item1/option1",
"children": null          },
          {
"key": "2",
"name": "Option 2",
"icon": null,
"url": "/sub2/item1/option2",
"children": null          },
        ]
      },
      {
"key": "g2",
"name": "item2",
"icon": "setting",
"children": [
          {
"key": "1",
"name": "Option 1",
"icon": null,
"url": "/sub2/item2/option1",
"children": null          },
          {
"key": "2",
"name": "Option 2",
"icon": null,
"url": "/sub2/item2/option2",
"children": null          },
        ]
      }]
  },
...];
  • 权限列表demo:
constresourceList= [
"/sub1/item1/option1",
"/sub1/item1/option2",
"/sub1/item2/option1",
"/sub2/item2/option1",
"/sub2/item2/option1",
...];

这里还有个问题,点击顶部菜单的时候,需要进入对应模块的第一个有权限的页面,因为把页面菜单都是层层嵌套的,所以,这里需要找出第一个有权限的页面就需要再做一点工作:

setMenu= (e) => {
// 筛选出第一个有权限的子菜单functionfirstAuthMenuItem(menus) {
// 这里使用reduce函数,将层层嵌套的菜单的所有 children 扁平化,都放在一个数组中,这样就能很方便的找到第一个有权限的子菜单constgetMenuChild=menuList=>menuList.reduce((totalArr, current) => (
totalArr.concat(Array.isArray(current.children) ?getMenuChild(current.children) : current)
      ), []);
returngetMenuChild(menus).find(item=>item.authority);
    }
this.setState(
      {
menuType: e,
      },
      () => {
const { menuArr } =this.state;
const { history } =this.props;
leturl;
// 点击顶部菜单,进入第一个有权限的子菜单switch (e) {
case'Navigation One':
constsystemMenu=menuArr.filter(item=>item.name==='Navigation One');
url=firstAuthMenuItem(systemMenu.children).url;
break;
case'Navigation Two':
constmessageMenu=menuArr.filter(item=>item.name==='Navigation Two');
url=firstAuthMenuItem(messageMenu.children).url;
break;
default: 
url='/document' ;
break;
        }
history.push(url);
      }
    );
  };

3. 路由的控制

之前做Vue项目是使用 vue-router 提供的路由守卫在跳转前后取消跳转的方式,来实现路由权限的控制。react-router 相对于 vue-router 来说更为灵活,轻巧,所以没有内置这些API,但是基于它的灵活性,可以实现类似导航守卫的功能。

1. 实现思路

主要思路是使用了 React 的 Render Props ,Render Props 相比于固定写死的组件来说更为灵活,可以动态的决定渲染的结果。正如官网所说:

render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

reacr-router 内置了 Render Props ,使用 来取代 ,根据路由权限,动态修改当前路由要渲染的结果。

2. 实现方案

  1. 首先,将所有的路由信息放在一起,构建一个路由表:
constrouterMap= [
  { path: `/home`, component: HomePage, exact: true },
  { path: `/test/page1`, component: Page1, exact: true },
  { path: `/test/page2`, component: Page2, auth: true }, // 白名单页面  { path: `/exception/403`, component: Exception, auth: true }
...];
  1. 这样,就能通过遍历的方式输出所有的路由信息,而且方便在遍历时进行一些统一操作。对不需要权限控制的页面组件,设置auth:true 即可设置白名单。
  2. 遍历路由信息,为有权限的路由进行赋权。
// 遍历路由,权限赋值constauthRouterMap=routerMap.forEach(item=> {
constitemCopy=Object.assign({}, item);
if (resourceList.includes(item.path)) {
itemCopy.auth=true;
  }
returnitemCopy;
});
  1. 使用 Route 组件的 render prop 渲染组件
...return (
<Switch><Routepath={`/`}
render={() => (
// 重定向至第一个有权限的界面<Redirectto={routerMap.find(item=>item.auth).path} />      )}
exact/>    {
routerMap.map((item, index) => (
<Routekey={index}
path={item.path}
exact={item.exact}
render={props=> {
if (item.auth) {
// 如果有权限就渲染对应的页面组件return (<item.component {...props} key={index} />);
            }
// 如果无权限就渲染重定向至403页面return<Redirectto={{ pathname: `/exception/403` }} />;          }}
/>      ))
    }
</Switch>);
...
  • 注意:如果使用了 render 函数,就不能再使用 component 的方式了,因为 component 的方式要优先于 renderrender 会被覆盖掉。
目录
相关文章
|
前端开发 数据安全/隐私保护
|
3月前
|
缓存 前端开发 JavaScript
React Hooks深度解析与最佳实践:提升函数组件能力的终极指南
🌟蒋星熠Jaxonic,前端探索者。专注React Hooks深度实践,从原理到实战,分享状态管理、性能优化与自定义Hook精髓。助力开发者掌握函数组件的无限可能,共赴技术星辰大海!
React Hooks深度解析与最佳实践:提升函数组件能力的终极指南
|
8月前
|
缓存 前端开发 数据安全/隐私保护
如何使用组合组件和高阶组件实现复杂的 React 应用程序?
如何使用组合组件和高阶组件实现复杂的 React 应用程序?
297 68
|
8月前
|
缓存 前端开发 Java
在 React 中,组合组件和高阶组件在性能方面有何区别?
在 React 中,组合组件和高阶组件在性能方面有何区别?
268 67
|
8月前
|
前端开发 JavaScript 安全
除了高阶组件和render props,还有哪些在 React 中实现代码复用的方法?
除了高阶组件和render props,还有哪些在 React 中实现代码复用的方法?
317 62
|
11月前
|
移动开发 前端开发 API
React 音频播放器组件 Audio Player
本文介绍如何使用React创建音频播放器组件,涵盖核心功能如播放/暂停、进度条、音量控制和时间显示。通过HTML5 `&lt;audio&gt;` 元素和React的声明式状态管理,实现交互式音频播放。常见问题包括控件不响应、进度条无法更新和音量控制失灵,并提供解决方案。此外,还讨论了浏览器兼容性、异步错误处理和性能优化等易错点及避免方法。
780 123
|
10月前
|
前端开发 JavaScript
除了使用Route组件,React Router还有其他方式处理404错误页面吗
除了使用Route组件,React Router还有其他方式处理404错误页面吗
277 58
|
10月前
|
前端开发
React 中高阶组件的原理是什么?
React 中高阶组件的原理是什么?
248 57
|
10月前
|
前端开发 开发者
除了函数组件和类组件,React 还有其他创建组件的方式吗?
除了函数组件和类组件,React 还有其他创建组件的方式吗?
196 57
|
10月前
|
前端开发
如何在React Router中定义404错误页面组件?
如何在React Router中定义404错误页面组件?
261 57