如何在React Redux应用程序中实现基于角色的限制/权限?

问题描述 投票:0回答:7

我有一个包含多个组件的 React-Redux-KoaJs 应用程序。我的用户角色也很少。现在我想仅向特定角色显示一些按钮、表格和 div,并对其他角色隐藏这些按钮、表格和 div。请记住,我不想隐藏整个组件,而只是隐藏组件的一部分。谁能帮我?预先感谢。

reactjs react-redux user-permissions role-based-access-control
7个回答
32
投票

您可以按照 @Eudald Arranz 的建议检查每个组件中的角色或权限。或者您可以编写一个组件来为您检查权限。例如:

import PropTypes from 'prop-types';
import { connect } from 'react-redux';

const ShowForPermissionComponent = (props) => {
    const couldShow = props.userPermissions.includes(props.permission);
    return couldShow ? props.children : null;
};

ShowForPermissionComponent.propTypes = {
    permission: PropTypes.string.isRequired,
    userPermissions: PropTypes.array.isRequired
};


const mapStateToProps = state => ({
    userPermissions: state.user.permission //<--- here you will get permissions for your user from Redux store
});

export const ShowForPermission = connect(mapStateToProps)(ShowForPermissionComponent);

然后你可以像这样使用这个组件:

import React from 'react';
import { ShowForPermission } from './ShowForPermission';

cons MyComponent = props => {
   return (
        <div>
            <ShowForPermission permission="DELETE">
                <button>Delete</button>
            </ShowForPermission>
        </div>
   );
}


16
投票

对此要小心。如果某些角色的操作很重要,您应该始终在后端验证它们。如果没有适当的验证,很容易更改存储在前端 redux 中的值,从而允许恶意使用角色。

如果您想继续采用以下可能的方法:

  • 将角色保存在您的减速器中
  • 将减速器绑定到组件上:
function mapStateToProps(state) {
  const { user_roles } = state;
  return { user_roles };
}

export default connect(mapStateToProps)(YourComponent);
  • 然后在您的组件中,您可以检查 user_roles 并相应地呈现操作:
render() {
    return (
      <div>
        {this.props.user_roles.role === "YOUR_ROLE_TO_CHECK" && <ActionsComponent />}
      </div>
    );
  }

仅当角色等于所需角色时才会渲染

ActionsComponent

再次强调,始终在后端验证角色!


12
投票

解决此问题的最佳实践是,简单地防止应用程序生成不必要的路线,而不是检查每条路线上的当前用户角色,最好只生成用户有权访问的路线。

所以正常的重新路由是: 控制整个视图:

const App = () => (
  <BrowserRouter history={history}>
    <Switch>
      <Route path="/Account" component={PrivateAccount} />
      <Route path="/Home" component={Home} />
    </Switch>
  </BrowserRouter>
  export default App;
);

基于用户角色的路由:

import { connect } from 'react-redux'
   // other imports ...
   const App = () => (
      <BrowserRouter history={history}>
        <Switch>
        {
          this.props.currentUser.role === 'admin' ?
            <>
          <Route path="/Account" exact component={PrivateAccount} />
          <Route path="/Home" exact component={Home} />
            </> 
            : 
          <Route path="/Home" exact component={Home} />
        }
        <Route component={fourOFourErroPage} />

        </Switch>
      </BrowserRouter>
      
const mapStateToProps = (state) => {
  return {
    currentUser: state.currentUser,
  }
}
export default connect(mapStateToProps)(App);

因此,具有管理员角色的用户将有权访问“帐户”页面,而其他用户将只能访问“主页”!如果任何用户尝试访问其他路由,就会出现404页面错误。 我希望我给出了一个有用的解决方案。

有关此方法的高级详细信息,您可以在 github 上查看此存储库:基于角色的访问控制与反应

仅隐藏演示组件:

{this.props.currentUser.role === 'admin' && <DeleteUser id={this.props.userId} /> }


6
投票

因此,我发现有一种替代且简单的方法可以在前端实现基于角色的访问(RBAC)。

在您的 redux 存储状态中,创建一个名为 Permissions 的对象(或者您可以将其命名为任何您喜欢的名称),如下所示:

const InitialState = {
  permissions: {}
};

然后在您的登录操作中,设置您想要提供的权限,如下所示:

InitialState['permissions'] ={
  canViewProfile: (role!=='visitor'),
  canDeleteUser: (role === 'coordinator' || role === 'admin')
  // Add more permissions as you like
}

在第一个权限中,您说如果您不是访客,则可以查看个人资料。 在第二个权限中,您说只有当您是管理员或协调员时才能删除用户。 并且这些变量将根据登录用户的角色保持 true 或 false。因此,在您的商店状态中,您将拥有一个权限对象,其中包含代表权限的键,它们的值将根据您的角色决定。

然后在您的组件中使用存储状态来获取权限对象。您可以使用连接来完成此操作,例如:

const mapStateToProps = (state) => {
  permissions : state.permissions
}

然后将这些道具连接到您的组件,例如:

export default connect(mapStateToProps,null)(ComponentName);

然后,您可以在组件内的任何您想要有条件显示的特定元素上使用这些道具,如下所示:

{(this.props.permissions.canDeleteUser) && <button onClick={this.deleteUser}>Delete User</button>}

上面的代码将确保仅当您有权删除用户时才会呈现删除用户按钮,即在您的商店状态权限对象中,canDeleteUser 的值为 true。

就是这样,您已经应用了基于角色的访问权限。您可以使用这种方法,因为它易于扩展和可变,因为您将在一个地方根据角色拥有所有权限。

希望,这有帮助!如果我错过了什么,请在评论中帮助我。 :-)


1
投票

我已经在这个 rbac-react-redux-aspnetcore 存储库中实现了这个。 如果有人想将 Redux 与 Context API 一起使用,那么下面的代码片段可能会有所帮助。

export const SecuedLink = ({ resource, text, url }) => {

  const userContext = useSelector(state => {
    return state.userContext;
  });    

  const isAllowed = checkPermission(resource, userContext);
  const isDisabled = checkIsDisabled(resource, userContext);

  return (isAllowed && <Link className={isDisabled ? "disable-control" : ""} to={() => url}>{text}</Link>)
}


const getElement = (resource, userContext) => {
    return userContext.resources
        && userContext.resources.length > 0
        && userContext.resources.find(element => element.name === resource);
}

export const checkPermission = (resource, userContext) => {
    const element = getElement(resource, userContext);
    return userContext.isAuthenticated && element != null && element.isAllowed;
}

export const checkIsDisabled = (resource, userContext) => {
    const element = getElement(resource, userContext);
    return userContext.isAuthenticated && element != null && element.isDisabled;
}

要使用上面的代码片段,我们可以像下面这样使用它

  <SecuedLink resource='link-post-edit' url={`/post-edit/${post.id}`} text='Edit'></SecuedLink>
  <SecuedLink resource='link-post-delete' url={`/post-delete/${post.id}`} text='Delete'></SecuedLink>

因此,根据角色,您不仅可以显示/隐藏元素,还可以启用/禁用它们。权限管理与react-client完全解耦,并在数据库中管理,这样您就不必为了支持新角色和新权限而一次又一次地部署代码。


0
投票

这篇文章正是您所需要的,无需任何库:

反应权限和角色


0
投票

步骤可以

  • 维护一个包含菜单和子菜单列表的 json 结构。
  • 迭代原始json结构以及每个菜单&&子菜单
    • 检查登录用户的角色
      • 积累可以渲染的新菜单列表。

请记住这是客户端。您仍然需要为每个 api 请求实施适当的授权机制,以防止每个菜单必须提供的未经授权的访问。

© www.soinside.com 2019 - 2024. All rights reserved.