Ant*_*vik 8 permissions roles reactjs redux
我在React和Redux中编写了一个单页面应用程序(带有Node.js后端).
我想实现基于角色的访问控制,并希望控制应用程序的某些部分(或子部分)的显示.
我将从Node.js获取权限列表,这只是具有这种结构的对象:
{
users: 'read',
models: 'write',
...
dictionaries: 'none',
}
Run Code Online (Sandbox Code Playgroud)
密钥是受保护的资源,
值是该资源的用户权限(之一:none,read,write).
我将它存储到redux状态.看起来很简单.
none权限将由react-router路由onEnter/onChange钩子或redux-auth-wrapper.看起来也很容易.
但是,将read/write权限应用于任何组件视图的最佳方法是什么(例如,如果用户有{ models: 'read' }权限,则隐藏模型组件中的编辑按钮).
我找到了这个解决方案,并为我的任务改变了一点:
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)
Run Code Online (Sandbox Code Playgroud)
哪里userPermissionsSelector会是这样的:(store) => store.userPermisisons与回报用户权限对象.
然后用以下包裹保护元素Check:
<Check resource="models" permission="write">
<Button>Edit model</Button>
</Check>
Run Code Online (Sandbox Code Playgroud)
因此,如果用户没有write权限,models则不会显示该按钮.
有人做过这样的事吗?有比这更"优雅"的解决方案吗?
谢谢!
PS当然,用户权限也将在服务器端进行检查.
好吧,我想我明白你想要什么.我做了一些适合我的事情,我喜欢我的方式,但我知道其他可行的解决方案都在那里.
我写的是HOC react-router风格.
基本上我有我的PermissionsProvider,我在其中初始化用户权限.我有另一个withPermissions 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 };
Run Code Online (Sandbox Code Playgroud)
好的,我知道这是很多代码.但这些是HOC(你可以在这里了解更多).
高阶组件(HOC)是React中用于重用组件逻辑的高级技术.HOC本身不是React API的一部分.它们是React组成性质的一种模式.具体地说,高阶组件是一个获取组件并返回新组件的函数.
基本上我这样做是因为我受到路由器反应的启发.每当你想知道一些路由的东西,你可以添加装饰器@withRouter,他们将道具注入你的组件.那么为什么不做同样的事呢?
//App render
return (
<PermissionsProvider permissions={permissions}>
<SomeStuff />
</PermissionsProvider>
);
Run Code Online (Sandbox Code Playgroud)
在SomeStuff里面你有一个广泛传播的工具栏来检查权限?
@withPermissions
export default class Toolbar extends React.Component {
render() {
const { permissions } = this.props;
return permissions.canDoStuff ? <RenderStuff /> : <HeCantDoStuff />;
}
}
Run Code Online (Sandbox Code Playgroud)
如果您不能使用装饰器,则可以像这样导出工具栏
export default withPermissions(Toolbar);
Run Code Online (Sandbox Code Playgroud)
这是我在实践中展示的代码框:
https://codesandbox.io/s/lxor8v3pkz
笔记:
小智 6
@lokuzt 建议的方法很棒。
您还可以进一步简化代码。
首先,每个受保护的组件都有一些需要满足渲染的要求。您需要定义一个函数,该函数将渲染要求和当前用户的凭据作为参数。它必须返回true或false。
function isSatisfied(requirement, credentials) {
if (...) {
return false;
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
此外,我们必须使用ReactJS的新上下文 API定义一个HOC(高阶组件)。
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>
);
}
};
}
Run Code Online (Sandbox Code Playgroud)
现在您可以装饰您的组件:
const requireAdmin = {...}; // <- this is your requirement
class AdminPanel extends Component {
...
}
export default protect(requireAdmin, AdminPanel);
Run Code Online (Sandbox Code Playgroud)
甚至第三方组件:
import {Button} from 'react-bootstrap';
const AdminButton = protect(requireAdmin, Button);
Run Code Online (Sandbox Code Playgroud)
凭据必须由 ReactJS 上下文 API 传递:
class MyApp extends Component {
render() {
const {credentials} = this.props;
<Provider value={credentials}>
...
<AdminPanel/>
<AdminButton>
Drop Database
</AdminButton>
...
</Provider>
}
}
Run Code Online (Sandbox Code Playgroud)
这是我在 github 上的扩展实现。
该演示也可用。
| 归档时间: |
|
| 查看次数: |
5324 次 |
| 最近记录: |