ReactJS RBAC実装アプローチ

エントリー



こんにちは親愛なる読者!



しばらく前(約1年前)、現在のユーザー権限に応じて、ReactJSでコンポーネントを条件付きでレンダリングする必要に直面しました。 最初に既製のソリューションと「ベストプラクティス」を探し始めました。 「 Reactでの役割ベースの認証 」という記事は、 高次コンポーネント (HOC)の使用に最も感銘を受けました。 しかし、残念ながら、私が満足させる解決策は見つかりませんでした。



どうやら、結局のところ、彼は何かを逃した...
...またはコンテキストの存在について知らなかった。 この記事を書いている時点で、stackoverflowで素晴らしい答えに出会いました。 私は非常によく似たソリューションになりました。



当時は、react-redux-connect(npmモジュール)に少し精通していましたが、connect関数で使用される装飾手法に強く夢中になりました。 接続デバイスの詳細な分析については、 こちらをご覧ください



ソリューションの説明



最初に、コンポーネントのレンダリングに関する決定を行うために必要な最小限の情報を決定する必要があります。 明らかに、レンダリングには何らかの条件を満たす必要があります(オプションとして、何らかの種類の権利があります-たとえば、新しいユーザーを追加する権利など)。 この条件を要件 (または英語の要件)と呼びます。 要件が満たされているかどうかを理解するために、現在の一連のユーザー権利資格情報)に基づいて判断できます 。 つまり、関数を定義するだけで十分です。



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」からの接続と同様):



 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を含む、スペルに関するすべてのコメントは、個人メッセージまたはプルリクエストの形式で送信してください。



All Articles