ReactJS RBAC实现方法

参赛作品


亲爱的读者您好!

前一段时间(大约一年),我面临着需要根据当前用户权限有条件地渲染ReactJS中的组件的需求。 我首先开始寻找现成的解决方案和“最佳实践”。 文章“ React中基于角色的授权 ”对高阶组件 (HOC)的使用印象最深。 但是,不幸的是,我没有找到令我满意的解决方案。

显然,他毕竟错过了一些东西……
...或者不知道上下文的存在。 在撰写本文时,我在stackoverflow中遇到了一个很好的答案 。 我最终得到了一个非常相似的解决方案。

那时,我对react-redux-connect(npm模块)有点熟悉,并且对连接功能中使用的修饰方法非常着迷。 可以在此处找到对连接设备的详细分析。

解决方案说明


首先,您需要确定决策呈现组件所需的最少信息。 显然,要进行渲染,必须满足一些条件(作为一种选择,有某种权利-例如,添加新用户的权利)。 我们将此条件称为要求 (或英文要求)。 要了解是否满足要求,我们可以基于一组当前的用户权限 - 凭据 。 也就是说,定义一个函数就足够了:

function isSatisfied(requirement, credentials) { if (...) { return false; } return true; } 

现在,我们或多或少地决定了渲染条件。 如何使用?

1.我们可以使用额头方法:

 const requirement = {...}; class App extends Component { render() { const {credentials} = this.props; return isSatisfied(requirement, credentials) && <TargetComponent>; } } 

2.我们可以走得更远,然后将目标组件包装在另一个组件中,这将验证需求:

 const requirement = {...}; class ProtectedTargetComponent extends Component { render() { const {credentials} = this.props; return ( isSatisfied(requirement, credentials) ? <TargetComponent {...this.props}> {this.props.children} </TargetComponent> : null ); } } class App extends Component { render() { const {credentials} = this.props; return <ProtectedTargetComponent/>; } } 

手动为每个目标组件编写包装器是很乏味的。 我们如何简化呢?

3.我们可以诉诸HOC机制(类似于“ react-redux-connect”中的connect):

 function protect(requirement, WrappedComponent) { return class extends Component { render() { const { credentials } = this.props; return ( isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const {credentials} = this.props; return ( ... <AdminButton credentials={credentials}> Add user </AdminButton> ... ); } } 

已经更好了,但仍然很麻烦-您需要在整个组件树中手动添加凭据。 该怎么办? 逻辑上假设当前用户的凭据是整个应用程序的全局对象。 然后,react-redux-connect再次起作用。 阅读有关该模块设备的文章后,我们发现它使用了一些ReactJS 上下文

4.使用上下文机制,我们得到最终的方法:

 const { Provider, Consumer } = React.createContext(); function protect(requirement, WrappedComponent) { return class extends Component { render() { return ( <Consumer> { credentials => isSatisfied(requirement, credentials) ? <WrappedComponent {...this.props}> {this.props.children} </WrappedComponent> : null } </Consumer> ); } } } ... const requireAdmin = {...}; const AdminButton = protect(requireAdmin, Button); ... class App extends Component { render() { const { credentials } = this.props; return ( <Provider value={credentials}> ... <AdminButton> Add user </AdminButton> ... </Provider> ); } } 

后记


这是对想法本身的简短论述。 基于此想法,实现了一个模块( githubnpm ),该模块提供了更多有趣的功能并且更易于嵌入(请参阅github中的README.md并使用该模块进行演示 )。

出于某种原因,我无法将创建的npm软件包放入演示中,因此我不得不在其中插入模块代码。 但是通过npm install react-rbac-guard安装的模块可以在本地运行(Chrome 69.0.3497.100)。 我怀疑问题出在构建方法中-我只是从具有类似用途的模块中复制了package.json和webpack.config.prod.js文件。

由于我不是前端开发人员,所以仍有许多未完成的工作(缺少测试, https: //codesandbox.io中的不可操作性,还有可能遗漏了其他要点)。 因此,如果有任何意见,建议或要求,欢迎您!

PPS:有关拼写的所有注释(包括README.md),请发送个人消息或以请求请求的形式发送。

Source: https://habr.com/ru/post/zh-CN428143/


All Articles