根据用户角色隐藏一些React组件子项
我正在编写React和Redux中的单页应用程序(带有Node.js后端)。
我想实现基于角色的访问控制,并希望控制应用程序的某些部分(或子部分)的显示。
我将从Node.js获取权限列表,它只是一个具有这种结构的对象:
{
users: 'read',
models: 'write',
...
dictionaries: 'none',
}
关键是保护资源,
值是此资源的用户权限(以下之一: none
, read
, write
)。
我正在将它存储到redux状态。 看起来很简单。
none
权限将通过react-router
路由onEnter/onChange
钩子或者redux-auth-wrapper
。 这似乎也很容易。
但是,对任何组件视图应用read/write
权限的最佳方式是什么(例如,如果用户具有{ models: 'read' }
权限,则隐藏Models组件中的编辑按钮)。
我找到了这个解决方案,并为我的任务改变了一下:
class Check extends React.Component {
static propTypes = {
resource: React.PropTypes.string.isRequired,
permission: React.PropTypes.oneOf(['read', 'write']),
userPermissions: React.PropTypes.object,
};
// Checks that user permission for resource is the same or greater than required
allowed() {
const permissions = ['read', 'write'];
const { permission, userPermissions } = this.props;
const userPermission = userPermissions[resource] || 'none';
return permissions.indexOf(userPermission) >= permissions.indexOf(permission)
}
render() {
if (this.allowed()) return { this.props.children };
}
}
export default connect(userPermissionsSelector)(Check)
其中userPermissionsSelector
将如下所示: (store) => store.userPermisisons
并返回用户权限对象。
然后用Check
包装受保护的元素:
<Check resource="models" permission="write">
<Button>Edit model</Button>
</Check>
所以如果用户没有models
的write
权限,该按钮将不会显示。
有没有人做过这样的事情? 有没有比这更“优雅”的解决方案?
谢谢!
PS当然,用户权限也会在服务器端进行检查。
好吧,我想我明白你想要什么。 我做了一些对我有用的事情,我喜欢我的方式,但我知道其他可行的解决方案都在那里。
我写的是HOC react-router风格。
基本上我有我的PermissionsProvider我初始化用户权限。 我有另一个使用Permissions HOC,它将我之前提供的权限注入到我的组件中。
因此,如果我需要检查特定组件中的权限,我可以轻松访问它们。
// PermissionsProvider.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import hoistStatics from "hoist-non-react-statics";
class PermissionsProvider extends React.Component {
static propTypes = {
permissions: PropTypes.array.isRequired,
};
static contextTypes = {
permissions: PropTypes.array,
};
static childContextTypes = {
permissions: PropTypes.array.isRequired,
};
getChildContext() {
// maybe you want to transform the permissions somehow
// maybe run them through some helpers. situational stuff
// otherwise just return the object with the props.permissions
// const permissions = doSomething(this.props.permissions);
// maybe add some validation methods
return { permissions: this.props.permissions };
}
render() {
return React.Children.only(this.props.children);
}
}
const withPermissions = Component => {
const C = (props, context) => {
const { wrappedComponentRef, ...remainingProps } = props;
return (
<Component permissions={context.permissions} {...remainingProps} ref={wrappedComponentRef} />
);
};
C.displayName = `withPermissions(${Component.displayName || Component.name})`;
C.WrappedComponent = Component;
C.propTypes = {
wrappedComponentRef: PropTypes.func
};
C.contextTypes = {
permissions: PropTypes.array.isRequired
};
return hoistStatics(C, Component);
};
export { PermissionsProvider as default, withPermissions };
好吧,我知道这是很多代码。 但这些都是HOC(你可以在这里了解更多)。
高阶组件(HOC)是React中用于重用组件逻辑的高级技术。 HOC本身不是React API的一部分。 它们是从React的构图本质中浮现出来的一种模式。 具体而言,高阶组件是一个接收组件并返回新组件的函数。
基本上我这么做是因为我受到了路由器反应的启发。 每当你想知道一些路由的东西,你可以添加装饰@withRouter ,他们将道具注入你的组件。 那么为什么不做同样的事情呢?
//App render
return (
<PermissionsProvider permissions={permissions}>
<SomeStuff />
</PermissionsProvider>
);
SomeStuff里面的某个地方有一个广泛分布的工具栏来检查权限吗?
@withPermissions
export default class Toolbar extends React.Component {
render() {
const { permissions } = this.props;
return permissions.canDoStuff ? <RenderStuff /> : <HeCantDoStuff />;
}
}
如果你不能使用装饰器,就像这样导出工具栏
export default withPermissions(Toolbar);
这里是我在实践中展示的一个codesandbox:
https://codesandbox.io/s/lxor8v3pkz
笔记:
上一篇: Hide some React component children depending on user role
下一篇: How to convert Unix time / time since the epoch to standard date and time?