J'ai une application React-Redux-KoaJs avec plusieurs composants. J'ai aussi peu de rôles d'utilisateur. Maintenant, je veux afficher quelques boutons, tableaux et div pour des rôles spécifiques et les cacher aux autres. N'oubliez pas que je ne veux pas masquer tout le composant, mais juste une partie des composants. Quelqu'un peut-il m'aider? Merci d'avance.
5 Réponses :
Soyez prudent avec ça. Si les actions de certains rôles sont importantes, vous devez toujours les valider dans votre backend. Il est facile de modifier les valeurs stockées dans redux au frontend permettant une utilisation malveillante des rôles s'il n'y a pas de validation appropriée.
Si vous souhaitez poursuivre une approche possible, c'est la suivante:
render() { return ( <div> {this.props.user_roles.role === "YOUR_ROLE_TO_CHECK" && <ActionsComponent />} </div> ); }
function mapStateToProps(state) { const { user_roles } = state; return { user_roles }; } export default connect(mapStateToProps)(YourComponent);
Cela rendra le ActionsComponent
uniquement lorsque le rôle est égal à celui souhaité.
Encore une fois, validez toujours les rôles de votre backend!
Merci d'avoir répondu. C'est une approche à suivre, mais si vous avez beaucoup de composants et d'éléments à afficher et à masquer, il devient vraiment difficile de le suivre. J'ai trouvé une approche alternative que je vais publier dans les réponses très bientôt. Vérifiez-le. Vos suggestions sont les bienvenues.
Vous pouvez vérifier le rôle ou l'autorisation dans chaque composant comme @Eudald Arranz l'a proposé. Ou vous pouvez écrire un composant qui vérifiera les autorisations pour vous. Par exemple:
import React from 'react'; import { ShowForPermission } from './ShowForPermission'; cons MyComponent = props => { return ( <div> <ShowForPermission permission="DELETE"> <button>Delete</button> </ShowForPermission> </div> ); }
puis vous pouvez utiliser ce composant comme ceci:
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);
Merci d'avoir répondu. J'apprécie vraiment la réponse. Mais j'ai trouvé une approche alternative, que je vais mettre dans les réponses très bientôt. Vérifiez-le. Vos suggestions sont les bienvenues.
Cette approche présente des inconvénients en termes de performances. Imaginez que vous avez plus de 150 éléments d'interface utilisateur ou plus dans votre page qui doivent vérifier les autorisations (boutons, sélections, cases à cocher, options de menu, onglets, etc.). Avec votre approche, vous props.userPermissions.includes
par appeler la méthode props.userPermissions.includes
150 fois ou plus. Vous itérez le même tableau pour chaque élément. Cela peut ralentir votre application.
@Green oui, vous avez raison. Vous pouvez enregistrer vos autorisations dans ES6 Set
, ou si vous n'aimez pas utiliser des collections mutables, vous pouvez utiliser Set
de immutable.js. Ou vous pouvez simplement utiliser un Object
où les clés - noms et valeurs d'autorisation - tout ce que vous voulez.
J'ai donc compris qu'il existe une approche alternative et simple pour implémenter l'accès basé sur les rôles (RBAC) sur le frontend.
Dans l'état de votre magasin redux, créez un objet appelé permissions (ou vous pouvez le nommer comme vous le souhaitez) comme ceci:
{(this.props.permissions.canDeleteUser) && <button onClick={this.deleteUser}>Delete User</button>}
Ensuite, lors de votre action de connexion, configurez les autorisations que vous souhaitez fournir comme suit:
export default connect(mapStateToProps,null)(ComponentName);
Dans la première autorisation, vous dites que vous pouvez afficher le profil si vous n'êtes pas un visiteur. Dans la deuxième autorisation, vous dites que vous ne pouvez supprimer un utilisateur que si vous êtes un administrateur ou un coordinateur. et ces variables seront vraies ou fausses sur la base du rôle de l'utilisateur connecté. Ainsi, dans l'état de votre magasin, vous aurez un objet d'autorisation avec des clés qui représentent des autorisations et leur valeur sera décidée en fonction de votre rôle.
Ensuite, dans votre composant, utilisez l'état du magasin pour obtenir l'objet d'autorisation. Vous pouvez le faire en utilisant connect comme:
const mapStateToProps = (state) => { permissions : state.permissions }
puis connectez ces accessoires à votre composant comme:
InitialState['permissions'] ={ canViewProfile: (role!=='visitor'), canDeleteUser: (role === 'coordinator' || role === 'admin') // Add more permissions as you like }
Ensuite, vous pouvez utiliser ces accessoires à l'intérieur de votre composant sur n'importe quel élément particulier que vous souhaitez afficher conditionnellement comme ceci:
const InitialState = { permissions: {} };
Le code ci-dessus s'assurera que le bouton de suppression d'utilisateur n'est rendu que si vous avez les autorisations de supprimer l'utilisateur, c'est-à-dire que dans l'objet des autorisations de l'état de votre magasin, la valeur de canDeleteUser est true.
Voilà, vous avez appliqué un accès basé sur les rôles. Vous pouvez utiliser cette approche car elle est facilement évolutive et modifiable, car vous aurez toutes les autorisations en fonction des rôles à un seul endroit.
J'espère que cela t'aides! Si j'ai manqué quelque chose, aidez-moi dans les commentaires. :-)
> il est facilement évolutif et mutable Il n'est pas facilement évolutif. Ce n'est bon que pour des cas d'utilisation simples comme le vôtre - quelques rôles et quelques autorisations. Tout ce qui est plus compliqué que cela (par exemple, différentes étapes de l'application avec des autorisations différentes pour les rôles à chaque étape) et vous finirez par avoir ceci: a && b || c && d || e
qui est difficile à gérer.
La meilleure pratique pour résoudre ce problème consiste simplement à empêcher l'application de générer des itinéraires inutiles, au lieu de vérifier le rôle de l'utilisateur actuel sur chaque route, il est bon de générer uniquement les routes auxquelles l'utilisateur a accès.
Ainsi, le réacheminement normal est: Pour contrôler la vue entière:
{this.props.currentUser.role === 'admin' && <DeleteUser id={this.props.userId} /> }
Routage basé sur le rôle de l'utilisateur:
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);
Ainsi, l'utilisateur avec un rôle d'administrateur aura accès à la page Compte et pour les autres utilisateurs auront accès à la page d'accueil uniquement! et si un utilisateur tente d'accéder à une autre route, l'erreur de page 404 apparaîtra. J'espère avoir donné une solution utile.
Pour des détails avancés sur cette approche, vous pouvez vérifier ce dépôt sur github: Contrôle d'accès basé sur les rôles avec react
Pour masquer uniquement un composant de présentation:
const App = () => ( <BrowserRouter history={history}> <Switch> <Route path="/Account" component={PrivateAccount} /> <Route path="/Home" component={Home} /> </Switch> </BrowserRouter> export default App; );
Cela semble bon pour les routes, mais dans chaque composant, il peut y avoir des éléments qui sont montrés à l'utilisateur à la base de son rôle.
Exactement, pour cela, vous pouvez simplement masquer le composant de présentation comme mentionné ci-dessus.
J'ai implémenté cela dans ce référentiel rbac-react-redux-aspnetcore . Si quelqu'un souhaite utiliser Redux avec l'API de contexte, l'extrait de code ci-dessous peut être utile.
<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>
Pour utiliser l'extrait ci-dessus, nous pouvons l'utiliser comme ci-dessous
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; }
Ainsi, selon le rôle, vous pouvez non seulement afficher / masquer l'élément, mais également les activer / désactiver. La gestion des autorisations est entièrement découplée du react-client et gérée dans la base de données afin que vous n'ayez pas à déployer le code encore et encore juste pour prendre en charge de nouveaux rôles et de nouvelles autorisations.