基于react_基于角色的授权|| React.js中基于角色的访问控制V-2

基于react

Yay, I have come up with another super cool story I would call it RBAC-V2 because it is my second story on the same topic, there was some reason behind version-2 that are listed below.

是的,我想出了另一个超级酷的故事,我将其称为RBAC-V2,因为这是我的第二个故事,也是同一个话题,下面列出了第2版背后的某些原因。

  1. Version 1 is tightly coupled with the project in which I have introduced this technique.

    第1版与我介绍了该技术的项目紧密结合。
  2. No child/nested route support.

    不支持子级/嵌套路线。
  3. No technique to define roles in one place.

    没有一种技术可以在一处定义角色。
  4. Updating route access is complicated.

    更新路由访问很复杂。
  5. Have to maintain common routes for all roles.

    必须维护所有角色的通用路由。

Updates in version-2:

版本2中的更新:

  1. I have come up with a more generic approach so it’ll better serve the community.

    我想出了一个更通用的方法,以便更好地为社区服务。
  2. Added child/nested route support.

    添加了子级/嵌套路线支持。
  3. Define roles in one place so it’s easy to maintain.

    在一个位置定义角色,因此易于维护。
  4. Easy to update route access just by adding or removing role.

    只需添加或删除角色,即可轻松更新路由访问权限。
  5. If you skip the permission it’ll automatically accessible to everyone.

    如果您跳过此权限,所有人将自动访问。

The solution is exactly the same which I used in version-1 so if you didn’t check version-1, I would highly recommend going through so, you can better understand what exactly going on…

该解决方案与我在版本1中使用的完全相同,因此,如果您不检查版本1,我强烈建议您这样做,这样您可以更好地了解发生了什么……

The idea is simply prevent the app to generate unnecessary routes, rather checking the role on each route and showing fallback UI it would be a great idea to generate only the routes in which user have access, so if the user navigate manually to a route ie: (typing in the address bar) he/she will get 404 Not Found screen because route is not registered.

这个想法只是防止应用程序生成不必要的路由, 而是检查每个路由上的角色并显示后备UI,最好只生成用户有权访问的路由,因此,如果用户手动导航到某个路由,即:(在地址栏中输入),由于未注册路线,他/她将获得404 Not Found屏幕。

STOP STOP STOP
🙋🏻‍♂️ Stop Stop Stop
Stop‍♂️ Stop Stop Stop

你在说什么?
这真的有可能吗?
如果是,那怎么办? (What are you talking about?
is this really possible?
If yes then how?)

Yes, it is possible if you have heard about react-router 4 philosophy it supports dynamic routing and we can take advantage of it.

是的,如果您听说过react-router 4哲学 ,它有可能支持动态路由,那么我们可以利用它。

When we say dynamic routing, we mean routing that takes place as your app is rendering, not in a configuration or convention outside of a running app.

当我们说动态路由时,是指在您的应用渲染时发生的路由,而不是在运行的应用之外的配置或约定中进行。

Image for post
Let’s start
开始吧

Step #1Because we are looking for a role-based solution so let’s define the roles first, roles are nothing just a plain javascript object having key-value pair, below is the placeholder roles for my demo app you can replace these with yours.

步骤#1因为我们正在寻找基于角色的解决方案,所以让我们先定义角色,角色不只是具有键值对的普通javascript对象, 下面是我的演示应用程序的占位符,您可以将其替换为您的

Image for post
User Roles
用户角色
File: src/config/Rolesexport default {
SUPER_ADMIN: 'SUPER_ADMIN',
ADMIN: 'ADMIN',
MANAGER: 'MANAGER',
CUSTOMER: 'CUSTOMER',
GUEST: 'GUEST'};

Step #2Define the private routes configuration, route config object supports all the react-router’s route component props with some additional props ie: (title, permission, children) you can add or remove props from config object it means it is super customizable and support up to N nesting, child routes must follow the same parent shape, it means the config object is same for both there is no additional key for the child nor for a parent.

步骤#2定义专用路由配置,路由配置对象支持所有react-router的路由组件道具以及一些其他道具,例如:( 标题,权限,子代), 您可以从配置对象中添加或删除道具,这意味着它是超级可定制的为了支持最多N个嵌套,子路由必须遵循相同的父形状,这意味着配置对象相同,因为子对象和父对象都没有额外的键。

File: src/config/PrivateRoutesConfigimport { Roles } from 'config'// Componentsimport {
Module1,
Module2,
Module3,
ModuleN,
ModuleNChild1,
ModuleNChild2,
ModuleNChild3,
Dashboard,
Profile,
} from 'components';// TODO:/*
* 1. Make title optional
* 2. Make title multi type support ie: (string, node, react element)
* */
export default [
{
component: Module1,
path: '/',
title: 'Module - 1',
exact: true,
},
{
component: Module2,
path: '/module-2',
title: 'Module - 2',
},
{
component: Module3,
path: '/module-3',
title: 'Module - 3',
},
{
component: ModuleN,
path: '/module-n',
title: 'Module - N',
permission: [
Roles.SUPER_ADMIN,
Roles.ADMIN,
Roles.MANAGER
],
children: [
{
component: ModuleNChild1,
path: '/child-1',
title: 'Child - 1',
},
{
component: ModuleNChild2,
path: '/child-2',
title: 'Child - 2',
},
{
component: ModuleNChild3,
path: '/child-3',
title: 'Child - 3',
permission: [
Roles.SUPER_ADMIN,
Roles.ADMIN
]
}
]
},
{
component: Dashboard,
path: '/dashboard',
title: 'Dashboard',
permission: [
Roles.SUPER_ADMIN,
Roles.ADMIN,
],
},
{
component: Profile,
path: '/profile',
title: 'Profile',
permission: [
Roles.SUPER_ADMIN,
Roles.ADMIN,
Roles.MANAGER,
Roles.CUSTOMER
],
},
]

We have done with the configuration.

我们已经完成了配置。

What we have completed so far?

到目前为止,我们已经完成了什么?

one is the roles and the other is private routes array, right? 🤔🤔🤔 Yesss🆗 so it’s time to jump into the code…

一个是角色 ,另一个是私有路由数组,对吗? s是的🆗,现在该跳入代码了……

Image for post

Step #3

第三步

Let’s filter the private routes with user role or roles if your app supports multiple roles like our demo app supports

如果您的应用程序支持多个角色(例如我们的演示应用程序支持),让我们用一个或多个用户角色过滤私有路由

File: src/utils/indeximport { intersection } from 'lodash';export function isArrayWithLength(arr) {
return (Array.isArray(arr) && arr.length)
}export function getAllowedRoutes(routes) {
const roles = JSON.parse(localStorage.getItem('roles'));
return routes.filter(({ permission }) => {
if(!permission) return true;
else if(!isArrayWithLength(permission)) return true;
else return intersection(permission, roles).length;
});
}

now we have a utility method getAllowedRoutes in which we can pass routes array and it’ll return filtered routes array and pass that array into routes mapping component

现在我们有一个实用方法getAllowedRoutes 在其中我们可以传递路由数组,它将返回经过过滤的路由数组并将该数组传递到路由映射组件中

File: src/routes/PrivateRoutesimport React, { Fragment } from 'react';import { Redirect, useRouteMatch } from 'react-router-dom';import { getAllowedRoutes, isLoggedIn } from 'utils';import { PrivateRoutesConfig } from 'config';import { TopNav } from 'components/common';import MapAllowedRoutes from 'routes/MapAllowedRoutes';function PrivateRoutes() {
const match = useRouteMatch('/app');
let allowedRoutes = [];
if (isLoggedIn()) {
allowedRoutes = getAllowedRoutes(PrivateRoutesConfig);
} else {
return <Redirect to="/" />;
}
return (
<Fragment>
<TopNav
routes=
{allowedRoutes}
path={match.path}
className="bg-white"
/>
<MapAllowedRoutes
routes=
{allowedRoutes}
basePath="/app"
isAddNotFound
/>
</Fragment>
);
}export default PrivateRoutes;
Image for post

Step #4Now the last step is to render the filtered routes, I have created a component for that so the same component is used for rendering the parent as well as the child routes, this component requires filtered routes array and basePath as a prop.

步骤#4现在,最后一步是渲染过滤的路由,为此我创建了一个组件,因此该组件用于渲染父路由和子路由,该组件需要过滤路由数组和basePath作为道具。

File: src/routes/MapAllowedRoutesimport React, { memo } from 'react';import { Switch, Route, useRouteMatch } from 'react-router-dom';import { NotFound } from 'components/common';/*
* This is the route utility component used for generating
* routes and child routes it only requires routes array and basePath
*/
function MapAllowedRoutes({routes, basePath, isAddNotFound}) {
const match = useRouteMatch(basePath);
return (
<Switch>
{routes.map((route) => {
const {
path,
component: Component,
children,
title,
permission,
...rest
} = route; return (
<Route
{...rest}
key={path}
path={`${match.path}${path}`}
>
<Component children={children} />
</Route>
)
})}
{isAddNotFound && <Route><NotFound /></Route>}
</Switch>
)
}export default memo(MapAllowedRoutes);

If you have seen 👀 in a PrivateRoutes we just filtered private routes using the getAllowedRoutes utility method that we have created in step #3 and pass that filtered routes array into a route mapper component, route mapper is nothing just a reusable component used for a map over the routes array.

如果你已经在PrivateRoutes看到👀我们只是过滤使用getAllowedRoutes实用方法私人路线,我们在第3步创建并传递过滤路线阵列到路由映象组件,路由映象无非只是用于映射一个可重用组件在路由数组上。

We are done with the routing.

路由已经完成。

What do you say if I synchronize your main navigation with routes? 🤔

如果我将主导航与路线同步,该怎么说? 🤔

That would be awesome.You don’t need to put extra efforts on navigation, just create navigation from filtered routes array and it’ll automatically in sync with routes and also display as per user roles so that the user can’t see other’s navigation options if a user navigates manually to a route ie: (typing in the address bar) user will get 404 Not Found screen.😍

那将是真棒。 您无需在导航上花费额外的精力, 只需从过滤后的路线数组创建导航,它就会自动与路线同步,并按用户角色显示,这样,如果用户导航,则用户将看不到其他人的导航选项手动到一条路线,即:(在地址栏中键入)用户将获得404 Not Found屏幕。😍

Here is the code

这是代码

File: src/components/common/TopNavimport React, { memo } from 'react';import { Button } from 'react-bootstrap';import PropTypes from 'prop-types';import { Link, useHistory } from "react-router-dom";import { isLoggedIn } from 'utils';function TopNav(props) {
let history = useHistory();
function handleLogout() {
localStorage.removeItem('roles');
history.push('/');
}
return (
<div className={`w3-bar w3-padding w3-card ${props.className}`} >
<div className="w3-display-topleft w3-padding-large w3-xlarge">
RBAC-V2
</div>
<div className="w3-right">
{props.routes.map(({ path, title }) => (
<Link
key=
{path}
className="w3-bar-item"
to=
{`${props.prefix}${path}`}
>
{title}
</Link>
))}
{isLoggedIn() && <Button onClick={handleLogout}>Logout</Button> }
</div>
</div>
);
}
TopNav.propTypes = {
routes: PropTypes.arrayOf(
PropTypes.shape({
path: PropTypes.string.isRequired,
title: PropTypes.string.isRequired
})
).isRequired,
prefix: PropTypes.string,
className: PropTypes.string};
TopNav.defaultProps ={
prefix: '',
className: ''};export default memo(TopNav);
Image for post
Image for post

好处 (Benefits)

  1. Check route access only once when parent route renders

    父路线渲染时仅检查一次路线访问
  2. Generate only routes that user have access

    仅生成用户有权访问的路由
  3. Central roles and private routes configuration file

    中心角色和专用路由配置文件
  4. Easy to add/remove a role

    轻松添加/删除角色
  5. Easy to add/remove route access from user role

    易于从用户角色添加/删除路由访问
  6. Synchronization between routes and navigation

    路线与导航之间的同步
  7. Single + Multiple role support

    单人+多人支持

结论 (Conclusion)

Well, this is easy to maintain and scalable approach for achieving role-based access on routes as well as on navigation with support of multi roles and nested routes with permission

好吧,这是一种易于维护且可扩展的方法,用于在路由以及基于导航的导航上实现基于角色的访问,并支持多角色和嵌套路由并获得许可

链接 (Links)

Github: https://github.com/umair-khanzada/role-based-access-controlDemo App: https://umair-khanzada.github.io/role-based-access-control

GitHub: https : //github.com/umair-khanzada/role-based-access-control演示应用程序: https : //umair-khanzada.github.io/role-based-access-control

翻译自: https://medium.com/swlh/role-based-authorization-role-based-access-control-v-2-in-react-js-cb958e338f4b

基于react

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值