【郑州研发中心】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 会被覆盖掉。
目录
相关文章
|
前端开发 数据安全/隐私保护
|
9月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
450 0
|
9月前
|
存储 前端开发 JavaScript
【第34期】一文学会React组件传值
【第34期】一文学会React组件传值
95 0
|
9月前
|
前端开发
【第31期】一文学会用React Hooks组件编写组件
【第31期】一文学会用React Hooks组件编写组件
96 0
|
9月前
|
存储 前端开发 JavaScript
【第29期】一文学会用React类组件编写组件
【第29期】一文学会用React类组件编写组件
94 0
|
9月前
|
前端开发 开发者
【第26期】一文读懂React组件编写方式
【第26期】一文读懂React组件编写方式
79 0
|
9月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
160 0
|
9月前
|
存储 前端开发 中间件
React组件间的通信
React组件间的通信
71 1
|
9月前
|
前端开发 JavaScript API
React组件生命周期
React组件生命周期
139 1
|
9月前
|
存储 前端开发 JavaScript
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)
探索 React Hooks 的世界:如何构建出色的组件(下)